├── tests ├── config.nims ├── test2.nim ├── dueling_banjos.nim ├── dueling_banjos_json.nim └── test1.nim ├── .gitattributes ├── src ├── paramidi │ ├── tsf.c │ ├── constants.nim │ ├── tsf.nim │ └── tsf.h └── paramidi.nim ├── examples ├── config.nims ├── README.md ├── examples.nimble └── src │ ├── dueling_banjos.nim │ ├── common.nim │ ├── undone_in_sorrow.nim │ └── aeriths_theme.nim ├── paramidi.nimble ├── UNLICENSE └── README.md /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | **/*.c linguist-vendored 2 | **/*.h linguist-vendored 3 | -------------------------------------------------------------------------------- /src/paramidi/tsf.c: -------------------------------------------------------------------------------- 1 | #define TSF_IMPLEMENTATION 2 | #include "tsf.h" 3 | -------------------------------------------------------------------------------- /examples/config.nims: -------------------------------------------------------------------------------- 1 | when defined(linux): 2 | switch("passL", "-ldl -lm -lpthread") 3 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | To run each example: 2 | 3 | ``` 4 | nimble run dueling_banjos 5 | ``` 6 | 7 | ``` 8 | nimble run aeriths_theme 9 | ``` 10 | 11 | ``` 12 | nimble run undone_in_sorrow 13 | ``` 14 | -------------------------------------------------------------------------------- /paramidi.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.7.0" 4 | author = "oakes" 5 | description = "A library for making MIDI music" 6 | license = "Public Domain" 7 | srcDir = "src" 8 | 9 | 10 | 11 | # Dependencies 12 | 13 | requires "nim >= 1.2.6" 14 | -------------------------------------------------------------------------------- /examples/examples.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "oakes" 5 | description = "Paramidi examples" 6 | license = "Public Domain" 7 | srcDir = "src" 8 | bin = @["dueling_banjos", "aeriths_theme", "undone_in_sorrow"] 9 | 10 | 11 | # Dependencies 12 | 13 | requires "nim >= 1.2.6" 14 | requires "paramidi >= 0.7.0" 15 | requires "paramidi_soundfonts >= 0.2.0" 16 | requires "parasound >= 1.0.0" 17 | -------------------------------------------------------------------------------- /tests/test2.nim: -------------------------------------------------------------------------------- 1 | #? replace(sub="d♭", by="cx") | replace(sub="e♭", by="dx") | replace(sub="g♭", by="fx") | replace(sub="a♭", by="gx") | replace(sub="b♭", by="ax") 2 | 3 | import unittest 4 | import paramidi 5 | 6 | test "sharps and flats": 7 | const 8 | score1 = compile((piano, 1/4, cx, dx, fx, gx, ax, cx+1, dx+1, fx+1, gx+1, ax+1)) 9 | score2 = compile((piano, 1/4, d♭, e♭, g♭, a♭, b♭, d♭+1, e♭+1, g♭+1, a♭+1, b♭+1)) 10 | check score1 == score2 11 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /tests/dueling_banjos.nim: -------------------------------------------------------------------------------- 1 | import paramidi 2 | 3 | const 4 | measure1 = (1/16, b, +c, 1/8, +d, b, +c, a, b, g, a) 5 | measure2 = (1/16, g, g, 1/8, g, a, b, +c, +d, +c, 1/2, b) 6 | measure3 = (1/16, {d, -b, -g}, {d, -b, -g}, 7 | 1/8, {d, -b, -g}, {e, c, -g}, {d, -b, -g}) 8 | measure4 = (1/16, r, r, 1/8, g, r, d, r, g, g, d) 9 | measure5 = (1/4, g, 1/8, a, b, 1/4, g, 1/8, a, d) 10 | measure6 = (1/4, g, 1/8, a, b, 1/4, g, 1/8, {f, -a}, b, 1/4, c) 11 | score* = 12 | ((tempo: 80, octave: 3), 13 | (guitar, measure1), 14 | (banjo, measure1), 15 | (guitar, measure1), 16 | (guitar, 1/2, d, 1/8, g, g, a, b, g, b, 1/2, a), 17 | (banjo, 1/8, g, g, a, b, 1/2, g), 18 | (guitar, (octave: 2), measure2), 19 | (banjo, measure2), 20 | (guitar, (octave: 2), measure2), 21 | (banjo, measure2), 22 | (guitar, measure2), 23 | (banjo, measure2), 24 | (octave: 4), 25 | (guitar, measure3), 26 | (banjo, measure3), 27 | (guitar, measure3), 28 | (banjo, measure3), 29 | (guitar, (octave: 2), measure1), 30 | (banjo, (octave: 3), measure1), 31 | (tempo: 120, octave: 3), 32 | ((mode: concurrent), 33 | (banjo, measure1), 34 | (guitar, measure4)), 35 | ((mode: concurrent), 36 | (banjo, measure1), 37 | (guitar, measure4)), 38 | ((mode: concurrent), 39 | (banjo, measure1), 40 | (guitar, measure5)), 41 | ((mode: concurrent), 42 | (banjo, measure1), 43 | (guitar, measure6))) 44 | -------------------------------------------------------------------------------- /tests/dueling_banjos_json.nim: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | let 4 | measure1 = %*[1/16, "b", "c+", 1/8, "d+", "b", "c+", "a", "b", "g", "a"] 5 | measure2 = %*[1/16, "g", "g", 1/8, "g", "a", "b", "c+", "d+", "c+", 1/2, "b"] 6 | measure3 = %*[1/16, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "d", "b-", "g-"], 7 | 1/8, [{"mode": "concurrent"}, "d", "b-", "g-"], [{"mode": "concurrent"}, "e", "c", "g-"], 8 | [{"mode": "concurrent"}, "d", "b-", "g-"]] 9 | measure4 = %*[1/16, "r", "r", 1/8, "g", "r", "d", "r", "g", "g", "d"] 10 | measure5 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, "a", "d"] 11 | measure6 = %*[1/4, "g", 1/8, "a", "b", 1/4, "g", 1/8, [{"mode": "concurrent"}, "f", "a-"], "b", 1/4, "c"] 12 | score* = 13 | %*[{"tempo": 80, "octave": 3}, 14 | ["guitar", measure1], 15 | ["banjo", measure1], 16 | ["guitar", measure1], 17 | ["guitar", 1/2, "d", 1/8, "g", "g", "a", "b", "g", "b", 1/2, "a"], 18 | ["banjo", 1/8, "g", "g", "a", "b", 1/2, "g"], 19 | ["guitar", {"octave": 2}, measure2], 20 | ["banjo", measure2], 21 | ["guitar", {"octave": 2}, measure2], 22 | ["banjo", measure2], 23 | ["guitar", measure2], 24 | ["banjo", measure2], 25 | {"octave": 4}, 26 | ["guitar", measure3], 27 | ["banjo", measure3], 28 | ["guitar", measure3], 29 | ["banjo", measure3], 30 | ["guitar", {"octave": 2}, measure1], 31 | ["banjo", {"octave": 3}, measure1], 32 | {"tempo": 120, "octave": 3}, 33 | [{"mode": "concurrent"}, 34 | ["banjo", measure1], 35 | ["guitar", measure4]], 36 | [{"mode": "concurrent"}, 37 | ["banjo", measure1], 38 | ["guitar", measure4]], 39 | [{"mode": "concurrent"}, 40 | ["banjo", measure1], 41 | ["guitar", measure5]], 42 | [{"mode": "concurrent"}, 43 | ["banjo", measure1], 44 | ["guitar", measure6]]] 45 | -------------------------------------------------------------------------------- /tests/test1.nim: -------------------------------------------------------------------------------- 1 | import unittest 2 | import paramidi, paramidi/tsf 3 | from os import nil 4 | 5 | test "load soundfont": 6 | const sfPath = "../paramidi_soundfonts/src/paramidi_soundfonts/aspirin.sf2" 7 | static: assert os.fileExists(sfPath) 8 | var sf = tsf_load_filename(sfPath) 9 | tsf_set_output(sf, TSF_MONO, 44100, 0) #sample rate 10 | tsf_note_on(sf, 0, 60, 1.0f) #preset 0, middle C 11 | var halfSecond: array[22050, cshort] # synthesize 0.5 seconds 12 | tsf_render_short(sf, cast[ptr cshort](halfSecond.addr), halfSecond.len.cint, 0) 13 | 14 | from algorithm import nil 15 | 16 | proc sortEvents(events: var seq[Event]) = 17 | # since the order of notes in a chord can be unpredictable, 18 | # the only way we can compare the outputs below is by 19 | # sorting them so the result is deterministic 20 | algorithm.sort(events, proc (x, y: Event): int = 21 | if x.time < y.time: 22 | -1 23 | elif x.time > y.time: 24 | 1 25 | elif x.note.ord < y.note.ord: 26 | -1 27 | elif x.note.ord > y.note.ord: 28 | 1 29 | else: 30 | 0 31 | ) 32 | 33 | import json 34 | 35 | test "JSON simple": 36 | var 37 | score1 = compile((piano, 1/4, c, 2, d)) 38 | score2 = compile(%*["piano", 1/4, "c", 2, "d"]) 39 | score1.sortEvents 40 | score2.sortEvents 41 | check score1 == score2 42 | 43 | from dueling_banjos import nil 44 | from dueling_banjos_json import nil 45 | 46 | test "JSON dueling banjos": 47 | var 48 | score1 = compile(dueling_banjos.score) 49 | score2 = compile(dueling_banjos_json.score) 50 | score1.sortEvents 51 | score2.sortEvents 52 | check score1 == score2 53 | 54 | test "relative octaves": 55 | var 56 | score1 = compile((piano, c, (octave: 3), d, (octave: 5), e)) 57 | score2 = compile((piano, c, (octave: `-1`), d, (octave: `+2`), e)) 58 | score3 = compile(%*["piano", "c", {"octave": "-1"}, "d", {"octave": "+2"}, "e"]) 59 | score1.sortEvents 60 | score2.sortEvents 61 | score3.sortEvents 62 | check score1 == score2 63 | check score1 == score3 64 | -------------------------------------------------------------------------------- /examples/src/dueling_banjos.nim: -------------------------------------------------------------------------------- 1 | from common import nil 2 | import paramidi 3 | import paramidi/tsf 4 | import paramidi_soundfonts 5 | 6 | const 7 | measure1 = (1/16, b, +c, 1/8, +d, b, +c, a, b, g, a) 8 | measure2 = (1/16, g, g, 1/8, g, a, b, +c, +d, +c, 1/2, b) 9 | measure3 = (1/16, {d, -b, -g}, {d, -b, -g}, 10 | 1/8, {d, -b, -g}, {e, c, -g}, {d, -b, -g}) 11 | measure4 = (1/16, r, r, 1/8, g, r, d, r, g, g, d) 12 | measure5 = (1/4, g, 1/8, a, b, 1/4, g, 1/8, a, d) 13 | measure6 = (1/4, g, 1/8, a, b, 1/4, g, 1/8, {f, -a}, b, 1/4, c) 14 | score = 15 | ((tempo: 80, octave: 3), 16 | (guitar, measure1), 17 | (banjo, measure1), 18 | (guitar, measure1), 19 | (guitar, 1/2, d, 1/8, g, g, a, b, g, b, 1/2, a), 20 | (banjo, 1/8, g, g, a, b, 1/2, g), 21 | (guitar, (octave: 2), measure2), 22 | (banjo, measure2), 23 | (guitar, (octave: 2), measure2), 24 | (banjo, measure2), 25 | (guitar, measure2), 26 | (banjo, measure2), 27 | (octave: 4), 28 | (guitar, measure3), 29 | (banjo, measure3), 30 | (guitar, measure3), 31 | (banjo, measure3), 32 | (guitar, (octave: 2), measure1), 33 | (banjo, (octave: 3), measure1), 34 | (tempo: 120, octave: 3), 35 | ((mode: concurrent), 36 | (banjo, measure1), 37 | (guitar, measure4)), 38 | ((mode: concurrent), 39 | (banjo, measure1), 40 | (guitar, measure4)), 41 | ((mode: concurrent), 42 | (banjo, measure1), 43 | (guitar, measure5)), 44 | ((mode: concurrent), 45 | (banjo, measure1), 46 | (guitar, measure6))) 47 | 48 | when isMainModule: 49 | # get the sound font 50 | # in a release build, embed it in the binary. 51 | when defined(release): 52 | const soundfont = staticRead("paramidi_soundfonts/generaluser.sf2") 53 | var sf = tsf_load_memory(soundfont.cstring, soundfont.len.cint) 54 | # during dev, read it from the disk 55 | else: 56 | var sf = tsf_load_filename(paramidi_soundfonts.getSoundFontPath("generaluser.sf2")) 57 | # render the score 58 | const sampleRate = 44100 59 | tsf_set_output(sf, TSF_MONO, sampleRate, 0) 60 | var res = render[cshort](compile(score), sf, sampleRate) 61 | tsf_close(sf) 62 | # create the wav file and play it 63 | const 64 | writeToDisk = true # if false, the wav file will only exist in memory 65 | padding = 500f # add a half second so it doesn't cut off abruptly 66 | when writeToDisk: 67 | common.writeFile("output.wav", res.data, res.data.len.uint32, sampleRate) 68 | discard common.play("output.wav", int(res.seconds * 1000f + padding)) 69 | else: 70 | let wav = common.writeMemory(res.data, res.data.len.uint32, sampleRate) 71 | discard common.play(wav, int(res.seconds * 1000f + padding)) 72 | -------------------------------------------------------------------------------- /examples/src/common.nim: -------------------------------------------------------------------------------- 1 | import parasound/dr_wav 2 | import parasound/miniaudio 3 | import os 4 | 5 | proc play*(data: string | seq[uint8], sleepMsecs: int): bool = 6 | ## if `data` is a string, it is interpreted as a filename. 7 | ## if `data` is a byte sequence, it is interpreted as an in-memory buffer. 8 | var engine = newSeq[uint8](ma_engine_size()) 9 | if MA_SUCCESS != ma_engine_init(nil, engine[0].addr): 10 | return false 11 | when data is string: 12 | if MA_SUCCESS != ma_engine_play_sound(engine[0].addr, data, nil): 13 | ma_engine_uninit(engine[0].addr) 14 | return false 15 | os.sleep(sleepMsecs) 16 | ma_engine_uninit(engine[0].addr) 17 | elif data is seq[uint8]: 18 | var decoder = newSeq[uint8](ma_decoder_size()) 19 | if MA_SUCCESS != ma_decoder_init_memory(data[0].unsafeAddr, data.len.csize_t, nil, decoder[0].addr): 20 | ma_engine_uninit(engine[0].addr) 21 | return false 22 | var sound = newSeq[uint8](ma_sound_size()) 23 | if MA_SUCCESS != ma_sound_init_from_data_source(engine[0].addr, decoder[0].addr, 0, nil, sound[0].addr): 24 | discard ma_decoder_uninit(decoder[0].addr) 25 | ma_engine_uninit(engine[0].addr) 26 | return false 27 | if MA_SUCCESS != ma_sound_start(sound[0].addr): 28 | ma_sound_uninit(sound[0].addr) 29 | discard ma_decoder_uninit(decoder[0].addr) 30 | ma_engine_uninit(engine[0].addr) 31 | return false 32 | os.sleep(sleepMsecs) 33 | ma_sound_uninit(sound[0].addr) 34 | discard ma_decoder_uninit(decoder[0].addr) 35 | ma_engine_uninit(engine[0].addr) 36 | true 37 | 38 | proc writeFile*(filename: string, data: var openArray[cshort], numSamples: uint32, sampleRate: uint32) = 39 | var 40 | wav = newSeq[uint8](drwav_size()) 41 | format: drwav_data_format 42 | format.container = drwav_container_riff 43 | format.format = DR_WAVE_FORMAT_PCM 44 | format.channels = 1 45 | format.sampleRate = sampleRate 46 | format.bitsPerSample = 16 47 | doAssert drwav_init_file_write(wav[0].addr, filename, addr(format), nil) 48 | doAssert numSamples == drwav_write_pcm_frames(wav[0].addr, numSamples, data.addr) 49 | discard drwav_uninit(wav[0].addr) 50 | 51 | proc writeMemory*(data: var openArray[cshort], numSamples: uint32, sampleRate: uint32): seq[uint8] = 52 | var 53 | wav = newSeq[uint8](drwav_size()) 54 | format: drwav_data_format 55 | format.container = drwav_container_riff 56 | format.format = DR_WAVE_FORMAT_PCM 57 | format.channels = 1 58 | format.sampleRate = sampleRate 59 | format.bitsPerSample = 16 60 | var 61 | outputRaw: pointer 62 | outputSize: csize_t 63 | doAssert drwav_init_memory_write_sequential(wav[0].addr, outputRaw.addr, outputSize.addr, format.addr, numSamples, nil) 64 | doAssert numSamples == drwav_write_pcm_frames(wav[0].addr, numSamples, data[0].addr) 65 | doAssert outputSize > 0 66 | result = newSeq[uint8](outputSize) 67 | copyMem(result[0].addr, outputRaw, outputSize) 68 | drwav_free(outputRaw, nil) 69 | discard drwav_uninit(wav[0].addr) 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A Nim library for making MIDI music. It uses [TinySoundFont](https://github.com/schellingb/TinySoundFont) underneath. A musical score is modeled as a simple hierarchy of tuples. There are probably bugs...or maybe your music just sounds bad. It could be that. Think about it. 2 | 3 | ## Quick start 4 | 5 | The fastest way to get started is by cloning the [starter project](https://github.com/paranim/paramidi_starter). 6 | Or you can use [paramidib](https://pietroppeter.github.io/paramidib/). 7 | 8 | ## Documentation 9 | 10 | * Check out the `examples` dir 11 | * Watch the screencast: https://www.youtube.com/watch?v=k3B5mhHbcK0 12 | * Watch the NimConf talk: https://www.youtube.com/watch?v=cBqBfPRWla8 13 | * Look at this commented example: 14 | 15 | ```nim 16 | # first hit middle c on the piano 17 | (piano, c) 18 | 19 | # hit all twelve notes 20 | (piano, c, cx, d, dx, e, f, fx, g, gx, a, ax, b) 21 | 22 | # by default you're on the 4th octave, but you can change it with an attribute tuple 23 | (piano, (octave: 3), c, d, e, f) 24 | 25 | # attribute tuples let you change attributes for anything that comes after it 26 | # (the `+1` is a relative change, so e and f will be played at the 4th octave) 27 | (piano, (octave: 3), c, d, (octave: `+1`), e, f) 28 | 29 | # notes are 1/4 length by default, but you can change that too 30 | (piano, (octave: 3), c, d, (octave: `+1`, length: 1/2), e, f) 31 | 32 | # you have to change note lengths often so here's a shorthand 33 | (piano, (octave: 3), c, d, (octave: `+1`), 1/2, e, f) 34 | 35 | # you can change individual notes' relative octave with + or - before 36 | (piano, (octave: 3), c, d, 1/2, +e, +f) 37 | 38 | # a number following the + or - changes it by that many octaves 39 | (piano, (octave: 3), c, d, 1/2, e+2, f+2) 40 | 41 | # if there is no + or - before the number, it sets the note's absolute octave 42 | (piano, (octave: 3), c, d, 1/2, e2, f2) 43 | 44 | # with all that, we can write the first line of dueling banjos 45 | (guitar, (octave: 3), 1/8, b, +c, 1/4, +d, b, +c, a, b, g, a) 46 | 47 | # you can also set volume, which is helpful for dynamics 48 | (piano, (volume: 120), c, d, e, f) 49 | 50 | # chords are just notes in a set 51 | (piano, {c, e}) 52 | 53 | # you can change the length of chords just like single notes 54 | (guitar, (octave: 4), 55 | 1/8, {d, -b, -g}, {d, -b, -g}, 56 | 1/4, {d, -b, -g}, {e, c, -g}, {d, -b, -g}) 57 | 58 | # to play two instruments concurrently, set the "mode" attribute 59 | ((mode: concurrent), 60 | (banjo, (octave: 3), 1/16, b, +c, 1/8, +d, b, +c, a, b, g, a), 61 | (guitar, (octave: 3), 1/16, r, r, 1/8, g, r, d, r, g, g, d)) # the r means rest 62 | ``` 63 | 64 | Sharp notes are represented with an `x`, so `cx` is C sharp. Using Nim's [source code filters](https://nim-lang.org/docs/filters.html) you can easily support other symbols. Source code filters can even be used to support flat notes by automatically converting them to their sharp equivalent, as you can see in [this example](tests/test2.nim). 65 | 66 | You can also represent scores using Nim's json module, which allows you to manipulate them more dynamically. For example see [dueling_banjos_json.nim](https://github.com/paranim/paramidi/blob/master/tests/dueling_banjos_json.nim) (for comparison, see [dueling_banjos.nim](https://github.com/paranim/paramidi/blob/master/tests/dueling_banjos.nim) for the same score with the tuple-based syntax). 67 | -------------------------------------------------------------------------------- /examples/src/undone_in_sorrow.nim: -------------------------------------------------------------------------------- 1 | from common import nil 2 | import paramidi 3 | import paramidi/tsf 4 | import paramidi_soundfonts 5 | 6 | func longChord(chord: set[Note], otherNotes: tuple): auto = 7 | # play the given chord for a half note 8 | # while the other notes are played 9 | ((mode: concurrent), (1/2, chord), (1/8, r, otherNotes)) 10 | 11 | const 12 | over_yonder_in_the_graveyard = 13 | (1/8, g, a, 1/4, b, +d, 1/8, +d, +e, longChord({d, g, b, +e}, (1/8, +d, 1/4, b, +d)), 14 | 1/4, +e, 1/8, +e, +d, 1/4, b, 1/8, g, a, 1/4, b, 15 | longChord({d, g, b, +d}, (1/8, +g, 1/4, +d)), longChord({d, g, b, +d}, (1/8, +g, 1/4, b)),) 16 | where_the_wild_wildflowers_grow = 17 | (1/8, g, a, 1/4, b, +d, 1/8, +d, +e, longChord({d, g, b, +e}, (1/8, +d, 1/4, b, +d)), 18 | 1/4, +e, +fx, 1/8, +e, +d, b, a, 1/4, b, 19 | longChord({d, g, b, +d}, (1/8, +g, 1/4, +d)), longChord({d, g, b, +d}, (1/8, +g, 1/4, b)),) 20 | oh_there_they_laid_my_own_true_lover = 21 | (1/8, b, +d, a, +d, 1/4, g, 22 | 1/8, d, e, 1/4, {+g, +d}, 1/8, g, +d, +g, +d, 1/4, d, 23 | 1/8, g, a, a, g, g, 24 | 1/8, d, e, 1/4, {+g, +d}, 1/8, g, +d, +g, +d, 1/4, d,) 25 | shes_gone_from_me_forevermore = 26 | (1/8, b, +d, a, +d, 1/4, g, 27 | 1/8, d, e, 1/4, {+g, +d}, 1/8, g, +d, +g, +d, 1/4, d, 28 | 1/8, a, b, a, g, g, 29 | 1/8, d, e, 1/4, {+g, +d}, 1/8, g, +d, +g, +d, 1/4, d,) 30 | 31 | she_was_fairier_than_the_sweetest_flower = over_yonder_in_the_graveyard 32 | restless_as_the_wildest_wind = where_the_wild_wildflowers_grow 33 | oh_born_with_love_deep_as_the_ocean = oh_there_they_laid_my_own_true_lover 34 | that_was_the_girl_i_did_win = shes_gone_from_me_forevermore 35 | 36 | i_left_her_there_back_in_the_mountains = over_yonder_in_the_graveyard 37 | to_see_the_world_riches_to_gain = where_the_wild_wildflowers_grow 38 | when_i_returned_no_earthly_treasure = oh_there_they_laid_my_own_true_lover 39 | could_ease_this_heart_so_full_of_pain = shes_gone_from_me_forevermore 40 | 41 | from_way_up_high_upon_that_mountain = over_yonder_in_the_graveyard 42 | beneath_a_little_mound_of_clay = where_the_wild_wildflowers_grow 43 | the_girl_that_i_returned_to_marry = oh_there_they_laid_my_own_true_lover 44 | so_still_among_the_flowers_did_lay = shes_gone_from_me_forevermore 45 | 46 | ill_go_away_and_i_will_wander = over_yonder_in_the_graveyard 47 | lay_aside_my_earthly_gains = where_the_wild_wildflowers_grow 48 | and_ill_not_end_as_a_man_with_riches = oh_there_they_laid_my_own_true_lover 49 | undone_in_sorrow_ill_remain = shes_gone_from_me_forevermore 50 | 51 | score = 52 | ((octave: 3), banjo, 53 | 54 | over_yonder_in_the_graveyard, 55 | where_the_wild_wildflowers_grow, 56 | oh_there_they_laid_my_own_true_lover, 57 | shes_gone_from_me_forevermore, 58 | 59 | she_was_fairier_than_the_sweetest_flower, 60 | restless_as_the_wildest_wind, 61 | oh_born_with_love_deep_as_the_ocean, 62 | that_was_the_girl_i_did_win, 63 | 64 | i_left_her_there_back_in_the_mountains, 65 | to_see_the_world_riches_to_gain, 66 | when_i_returned_no_earthly_treasure, 67 | could_ease_this_heart_so_full_of_pain, 68 | 69 | from_way_up_high_upon_that_mountain, 70 | beneath_a_little_mound_of_clay, 71 | the_girl_that_i_returned_to_marry, 72 | so_still_among_the_flowers_did_lay, 73 | 74 | ill_go_away_and_i_will_wander, 75 | lay_aside_my_earthly_gains, 76 | and_ill_not_end_as_a_man_with_riches, 77 | undone_in_sorrow_ill_remain, 78 | undone_in_sorrow_ill_remain, 79 | ) 80 | 81 | when isMainModule: 82 | # get the sound font 83 | # in a release build, embed it in the binary. 84 | when defined(release): 85 | const soundfont = staticRead("paramidi_soundfonts/generaluser.sf2") 86 | var sf = tsf_load_memory(soundfont.cstring, soundfont.len.cint) 87 | # during dev, read it from the disk 88 | else: 89 | var sf = tsf_load_filename(paramidi_soundfonts.getSoundFontPath("generaluser.sf2")) 90 | # render the score 91 | const sampleRate = 44100 92 | tsf_set_output(sf, TSF_MONO, sampleRate, 0) 93 | var res = render[cshort](compile(score), sf, sampleRate) 94 | tsf_close(sf) 95 | # create the wav file and play it 96 | const 97 | writeToDisk = true # if false, the wav file will only exist in memory 98 | padding = 500f # add a half second so it doesn't cut off abruptly 99 | when writeToDisk: 100 | common.writeFile("output.wav", res.data, res.data.len.uint32, sampleRate) 101 | discard common.play("output.wav", int(res.seconds * 1000f + padding)) 102 | else: 103 | let wav = common.writeMemory(res.data, res.data.len.uint32, sampleRate) 104 | discard common.play(wav, int(res.seconds * 1000f + padding)) 105 | -------------------------------------------------------------------------------- /examples/src/aeriths_theme.nim: -------------------------------------------------------------------------------- 1 | from common import nil 2 | import paramidi 3 | import paramidi/tsf 4 | import paramidi_soundfonts 5 | 6 | const 7 | score = 8 | (piano, 9 | (tempo: 74), 10 | # 1 11 | (1/8, {-d, -a, e, fx}, a, 12 | 1/2, {fx, +d}, 13 | 1/8, {-e, e, +c}, a, 14 | 1/2, {c, e}, 15 | 1/8, {-d, -a, e, fx}, a, +d, +cx, +e, +d, b, +cx, 16 | 1/2, {-e, c, a}, 1/2, {c, e}), 17 | # 5 18 | (1/8, {-d, -a, e, fx}, a, 19 | 1/4, {fx, +d}, e, d, 20 | (tempo: 140), 21 | 1/8, {a-2, c, +c}, a, 22 | 1/4, {c, e}, -b, -a), 23 | # 7 24 | (1/8, d, e, {-d, d, fx, +d}, -a, d, -a, fx, -a, 25 | d, -a, -d, -a, d, 26 | 1/4, {d, fx}, 27 | 1/8, {-d, d}, {-cx, e}, {b-2, fx}, -fx, d, -b, 28 | 1/4, d, 29 | 1/8, {-fx, -a, d, fx}, g, {a-2, a}, -fx, cx, -a, 30 | 1/4, {cx, fx}, 31 | 1/8, {-g, d, g, b}, +cx, {b-2, +d}, -d, 32 | {-b, g, b}, -g, 33 | 1/4, -b, {-g, e, g}, 34 | 1/16, {-d, fx, a}, b, 35 | 1/8, {-a, a}, e, -a, 36 | 1/4, fx), 37 | # 15 38 | (1/8, {-d, a}, fx, {a-2, cx}, -fx, -a, cx, {a-2, +cx}, 39 | -fx, -a, cx, {a-2, b}, -fx, -a, fx, {a-2, a}, -fx), 40 | # 17 41 | (1/8, {-a, b}, a, {b-2, d}, -d, -a, -b, {-d, b}, -a, 42 | -b, d, {-d, a}, -a, -b, d, 43 | {g, -d}, -a), 44 | # 19 45 | (1/8, {a, -b}, fx, {a-2, cx}, -gx, -a, cx, {a-2, +cx}, 46 | -gx, -a, cx, {b, a-2}, -gx, -a, cx, 47 | {-fx, a}, -gx), 48 | # 21 49 | (1/8, {-a, b}, +cx, {-g, +d}, d, g, a, b, +d, 50 | 1/4, {fx, b, +d}, 51 | 1/8, -d, -g, 52 | 1/4, {-b, g, +d}, {-g, a, +cx}), 53 | # 23 54 | (1/4, {-g, g, b}, {-d, d, fx, a}, 55 | 1/8, -d, -a, {e, fx}, -a, {e, fx}, -a, 56 | -d, -g, {b, +d}, -d, {a, +cx}, -d, 57 | {g, b}, -d, {-a, +cx}), 58 | # 25 59 | (1/8, -d, 60 | 1/4, {-a, +cx}, 61 | 1/8, a-2, +fx, {a-2, +fx}, cx, 62 | 1/4, fx, 63 | 1/8, b-2, -d, {a, +cx}, -d, {g, b}, -d, 64 | 1/4, {-a, +cx}, 65 | 1/2, {-e, d, g, a}, {-e, d, e}, {a-2, -g, d, e}, 66 | 1/4, {a-2, -a, cx}, 67 | 1/8, {e, d}), 68 | # 27 69 | (1/8, {fx, a}, {-g, g, b}, {fx, a}, {e, g}, 70 | 1/4, {d, fx}, 71 | 1/8, -d, -g, -b, d, g, 72 | 1/4, b, 73 | 1/8, {-g, g, b}, {fx, a}, {d, fx}), 74 | # 31 75 | (1/8, {e, g}, {-d, fx}, -a, d, fx, 76 | 1/2, +d, 77 | 1/4, +d, +cx, b), 78 | # 33 79 | (1/2, {-e, d, g, a}, {-fx, -a, cx, e}, 80 | 1/16, e, fx, 81 | 1/8, {-g, g}, fx, e, 82 | 1/2, d), 83 | # 35 84 | (1/8, -g, +d, b, a, +fx, +cx, b, g, 85 | {-d, -a, e, fx}, a, 86 | 1/2, {fx, +d}), 87 | # 37 88 | ((tempo: 156), 89 | 1/8, {a-2, c, +c}, a, 90 | 1/4, {c, e}, -b, -a), 91 | # 38 92 | (tempo: 80), 93 | (1/8, d, e, 94 | 1/4, {d, ax-2}, -g, 95 | 1/8, {f, -ax}, e, 96 | d, e, {d, ax-2}, -d, -g, (1/4, -a), -g, 97 | -ax, d, {+d, fx, d, -d}, -a, d, fx, d), 98 | # 41 99 | (1/8, d, -a, -d, -a, d, -a, fx, -a, 100 | 1/4, d, 1/2, -a), 101 | # 43 102 | (1/16, +d, d, fx, +cx, d, fx, 103 | 1/8, b, 1/2, {a, g, d, -e}, 104 | {e, cx, -a, -fx}, 1/16, e, fx, 105 | 1/4, {g, -g}, 1/8, {+f, cx}, {+e, +c}), 106 | # 45 107 | (tempo: 72), 108 | (1/8, {+d, b}, 1/2, {g, -b, -g}, 1/4, {b, d, -a}, a-2), 109 | # 46 110 | (tempo: 66), 111 | (1/4, {+d, fx, d, -d}, 1/8, -a, 1, {fx, e, -a, -d})) 112 | 113 | when isMainModule: 114 | # get the sound font 115 | # in a release build, embed it in the binary. 116 | when defined(release): 117 | const soundfont = staticRead("paramidi_soundfonts/generaluser.sf2") 118 | var sf = tsf_load_memory(soundfont.cstring, soundfont.len.cint) 119 | # during dev, read it from the disk 120 | else: 121 | var sf = tsf_load_filename(paramidi_soundfonts.getSoundFontPath("generaluser.sf2")) 122 | # render the score 123 | const sampleRate = 44100 124 | tsf_set_output(sf, TSF_MONO, sampleRate, 0) 125 | var res = render[cshort](compile(score), sf, sampleRate) 126 | tsf_close(sf) 127 | # create the wav file and play it 128 | const 129 | writeToDisk = true # if false, the wav file will only exist in memory 130 | padding = 500f # add a half second so it doesn't cut off abruptly 131 | when writeToDisk: 132 | common.writeFile("output.wav", res.data, res.data.len.uint32, sampleRate) 133 | discard common.play("output.wav", int(res.seconds * 1000f + padding)) 134 | else: 135 | let wav = common.writeMemory(res.data, res.data.len.uint32, sampleRate) 136 | discard common.play(wav, int(res.seconds * 1000f + padding)) 137 | -------------------------------------------------------------------------------- /src/paramidi/constants.nim: -------------------------------------------------------------------------------- 1 | import sets 2 | 3 | const 4 | notes* = [ 5 | "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b", 6 | "r", 7 | "c+", "c+1", "c+2", "c+3", "c+4", "c+5", "c+6", 8 | "c-", "c-1", "c-2", "c-3", "c-4", "c-5", "c-6", 9 | "c1", "c2", "c3", "c4", "c5", "c6", "c7", 10 | "c#+", "c#+1", "c#+2", "c#+3", "c#+4", "c#+5", "c#+6", 11 | "c#-", "c#-1", "c#-2", "c#-3", "c#-4", "c#-5", "c#-6", 12 | "c#1", "c#2", "c#3", "c#4", "c#5", "c#6", "c#7", 13 | "d+", "d+1", "d+2", "d+3", "d+4", "d+5", "d+6", 14 | "d-", "d-1", "d-2", "d-3", "d-4", "d-5", "d-6", 15 | "d1", "d2", "d3", "d4", "d5", "d6", "d7", 16 | "d#+", "d#+1", "d#+2", "d#+3", "d#+4", "d#+5", "d#+6", 17 | "d#-", "d#-1", "d#-2", "d#-3", "d#-4", "d#-5", "d#-6", 18 | "d#1", "d#2", "d#3", "d#4", "d#5", "d#6", "d#7", 19 | "e+", "e+1", "e+2", "e+3", "e+4", "e+5", "e+6", 20 | "e-", "e-1", "e-2", "e-3", "e-4", "e-5", "e-6", 21 | "e1", "e2", "e3", "e4", "e5", "e6", "e7", 22 | "f+", "f+1", "f+2", "f+3", "f+4", "f+5", "f+6", 23 | "f-", "f-1", "f-2", "f-3", "f-4", "f-5", "f-6", 24 | "f1", "f2", "f3", "f4", "f5", "f6", "f7", 25 | "f#+", "f#+1", "f#+2", "f#+3", "f#+4", "f#+5", "f#+6", 26 | "f#-", "f#-1", "f#-2", "f#-3", "f#-4", "f#-5", "f#-6", 27 | "f#1", "f#2", "f#3", "f#4", "f#5", "f#6", "f#7", 28 | "g+", "g+1", "g+2", "g+3", "g+4", "g+5", "g+6", 29 | "g-", "g-1", "g-2", "g-3", "g-4", "g-5", "g-6", 30 | "g1", "g2", "g3", "g4", "g5", "g6", "g7", 31 | "g#+", "g#+1", "g#+2", "g#+3", "g#+4", "g#+5", "g#+6", 32 | "g#-", "g#-1", "g#-2", "g#-3", "g#-4", "g#-5", "g#-6", 33 | "g#1", "g#2", "g#3", "g#4", "g#5", "g#6", "g#7", 34 | "a+", "a+1", "a+2", "a+3", "a+4", "a+5", "a+6", 35 | "a-", "a-1", "a-2", "a-3", "a-4", "a-5", "a-6", 36 | "a1", "a2", "a3", "a4", "a5", "a6", "a7", 37 | "a#+", "a#+1", "a#+2", "a#+3", "a#+4", "a#+5", "a#+6", 38 | "a#-", "a#-1", "a#-2", "a#-3", "a#-4", "a#-5", "a#-6", 39 | "a#1", "a#2", "a#3", "a#4", "a#5", "a#6", "a#7", 40 | "b+", "b+1", "b+2", "b+3", "b+4", "b+5", "b+6", 41 | "b-", "b-1", "b-2", "b-3", "b-4", "b-5", "b-6", 42 | "b1", "b2", "b3", "b4", "b5", "b6", "b7", 43 | ] 44 | instruments* = [ 45 | # Piano 46 | "piano", # acoustic-grand-piano 47 | "bright-acoustic-piano", 48 | "electric-grand-piano", 49 | "honky-tonk-piano", 50 | "electric-piano-1", 51 | "electric-piano-2", 52 | "harpsichord", 53 | "clavinet", 54 | # Chromatic Percussion 55 | "celesta", 56 | "glockenspiel", 57 | "music-box", 58 | "vibraphone", 59 | "marimba", 60 | "xylophone", 61 | "tubular-bells", 62 | "dulcimer", 63 | # Organ 64 | "drawbar-organ", 65 | "percussive-organ", 66 | "rock-organ", 67 | "organ", # church-organ 68 | "reed-organ", 69 | "accordion", 70 | "harmonica", 71 | "tango-accordion", 72 | # Guitar 73 | "guitar", # acoustic-guitar-nylon 74 | "acoustic-guitar-steel", 75 | "electric-guitar-jazz", 76 | "electric-guitar-clean", 77 | "electric-guitar-palm-muted", 78 | "electric-guitar-overdrive", 79 | "electric-guitar-distorted", 80 | "electric-guitar-harmonics", 81 | # Bass 82 | "acoustic-bass", 83 | "electric-bass", # electric-bass-finger 84 | "electric-bass-pick", 85 | "fretless-bass", 86 | "bass-slap", 87 | "bass-pop", 88 | "synth-bass-1", 89 | "synth-bass-2", 90 | # Strings 91 | "violin", 92 | "viola", 93 | "cello", 94 | "contrabass", 95 | "tremolo-strings", 96 | "pizzicato-strings", 97 | "harp", # orchestral-harp 98 | "timpani", 99 | # Ensemble 100 | "string-ensemble-1", 101 | "string-ensemble-2", 102 | "synth-strings-1", 103 | "synth-strings-2", 104 | "choir-aahs", 105 | "voice-oohs", 106 | "synth-voice", 107 | "orchestra-hit", 108 | # Brass 109 | "trumpet", 110 | "trombone", 111 | "tuba", 112 | "muted-trumpet", 113 | "french-horn", 114 | "brass-section", 115 | "synth-brass-1", 116 | "synth-brass-2", 117 | # Reed 118 | "soprano-sax", 119 | "alto-sax", 120 | "tenor-sax", 121 | "baritone-sax", 122 | "oboe", 123 | "english-horn", 124 | "bassoon", 125 | "clarinet", 126 | # Pipe 127 | "piccolo", 128 | "flute", 129 | "recorder", 130 | "pan-flute", 131 | "bottle", 132 | "shakuhachi", 133 | "whistle", 134 | "ocarina", 135 | # Synth Lead 136 | "square-lead", 137 | "saw-wave", 138 | "calliope-lead", 139 | "chiffer-lead", 140 | "charang", 141 | "solo-vox", 142 | "fifths", 143 | "bass-and-lead", 144 | # Synth Pad 145 | "synth-pad-new-age", 146 | "synth-pad-warm", 147 | "synth-pad-polysynth", 148 | "synth-pad-choir", 149 | "synth-pad-bowed", 150 | "synth-pad-metallic", 151 | "synth-pad-halo", 152 | "synth-pad-sweep", 153 | # Synth Effects 154 | "fx-rain", 155 | "fx-soundtrack", 156 | "fx-crystal", 157 | "fx-atmosphere", 158 | "fx-brightness", 159 | "fx-goblins", 160 | "fx-echoes", 161 | "fx-sci-fi", 162 | # Ethnic 163 | "sitar", 164 | "banjo", 165 | "shamisen", 166 | "koto", 167 | "kalimba", 168 | "bagpipes", 169 | "fiddle", 170 | "shehnai", 171 | # Percussive 172 | "tinkle-bell", 173 | "agogo", 174 | "steel-drums", 175 | "woodblock", 176 | "taiko-drum", 177 | "melodic-tom", 178 | "synth-drum", 179 | "reverse-cymbal", 180 | # Sound Effects 181 | "guitar-fret-noise", 182 | "breath-noise", 183 | "seashore", 184 | "bird-tweet", 185 | "telephone-ring", 186 | "helicopter", 187 | "applause", 188 | "gun-shot", 189 | ] 190 | relativeOctaves* = [ 191 | "+1", "+2", "+3", "+4", "+5", "+6", 192 | "-1", "-2", "-3", "-4", "-5", "-6", 193 | ] 194 | modes* = ["sequential", "concurrent"] 195 | noteSet* = notes.toHashSet 196 | instrumentSet* = instruments.toHashSet 197 | modeSet* = modes.toHashSet 198 | relativeOctaveSet* = relativeOctaves.toHashSet 199 | -------------------------------------------------------------------------------- /src/paramidi/tsf.nim: -------------------------------------------------------------------------------- 1 | {.compile: "tsf.c".} 2 | 3 | type tsf* = object 4 | 5 | proc tsf_load_filename*(filename: cstring): ptr tsf {.cdecl, importc.} 6 | ## Load a SoundFont from a block of memory 7 | 8 | proc tsf_load_memory*(buffer: pointer; size: cint): ptr tsf {.cdecl, importc.} 9 | ## Stream structure for the generic loading 10 | 11 | type 12 | tsf_stream* {.bycopy.} = object 13 | data*: pointer ## Custom data given to the functions as the first parameter 14 | ## Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes) 15 | read*: proc (data: pointer; `ptr`: pointer; size: cuint): cint ## Function pointer will be called to skip ahead over 'count' bytes (returns 1 on success, 0 on error) 16 | skip*: proc (data: pointer; count: cuint): cint 17 | 18 | 19 | ## Generic SoundFont loading method using the stream structure above 20 | 21 | proc tsf_load*(stream: ptr tsf_stream): ptr tsf {.cdecl, importc.} 22 | ## Free the memory related to this tsf instance 23 | 24 | proc tsf_close*(f: ptr tsf) {.cdecl, importc.} 25 | ## Stop all playing notes immediatly and reset all channel parameters 26 | 27 | proc tsf_reset*(f: ptr tsf) {.cdecl, importc.} 28 | ## Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont 29 | 30 | proc tsf_get_presetindex*(f: ptr tsf; bank: cint; preset_number: cint): cint {.cdecl, importc.} 31 | ## Returns the number of presets in the loaded SoundFont 32 | 33 | proc tsf_get_presetcount*(f: ptr tsf): cint {.cdecl, importc.} 34 | ## Returns the name of a preset index >= 0 and < tsf_get_presetcount() 35 | 36 | proc tsf_get_presetname*(f: ptr tsf; preset_index: cint): cstring {.cdecl, importc.} 37 | ## Returns the name of a preset by bank and preset number 38 | 39 | proc tsf_bank_get_presetname*(f: ptr tsf; bank: cint; preset_number: cint): cstring {.cdecl, importc.} 40 | ## Supported output modes by the render methods 41 | 42 | type 43 | TSFOutputMode* = enum ## Two channels with single left/right samples one after another 44 | TSF_STEREO_INTERLEAVED, ## Two channels with all samples for the left channel first then right 45 | TSF_STEREO_UNWEAVED, ## A single channel (stereo instruments are mixed into center) 46 | TSF_MONO 47 | 48 | 49 | ## Thread safety: 50 | ## Your audio output which calls the tsf_render* functions will most likely 51 | ## run on a different thread than where the playback tsf_note* functions 52 | ## are called. In which case some sort of concurrency control like a 53 | ## mutex needs to be used so they are not called at the same time. 54 | ## Alternatively, you can pre-allocate a maximum number of voices that can 55 | ## play simultaneously by calling tsf_set_max_voices after loading. 56 | ## That way memory re-allocation will not happen during tsf_note_on and 57 | ## TSF should become mostly thread safe. 58 | ## There is a theoretical chance that ending notes would negatively influence 59 | ## a voice that is rendering at the time but it is hard to say. 60 | ## Also be aware, this has not been tested much. 61 | ## Setup the parameters for the voice render methods 62 | ## outputmode: if mono or stereo and how stereo channel data is ordered 63 | ## samplerate: the number of samples per second (output frequency) 64 | ## global_gain_db: volume gain in decibels (>0 means higher, <0 means lower) 65 | 66 | proc tsf_set_output*(f: ptr tsf; outputmode: TSFOutputMode; samplerate: cint; 67 | global_gain_db: cfloat) {.cdecl, importc.} 68 | ## Set the global gain as a volume factor 69 | ## global_gain: the desired volume where 1.0 is 100% 70 | 71 | proc tsf_set_volume*(f: ptr tsf; global_gain: cfloat) {.cdecl, importc.} 72 | ## Set the maximum number of voices to play simultaneously 73 | ## Depending on the soundfond, one note can cause many new voices to be started, 74 | ## so don't keep this number too low or otherwise sounds may not play. 75 | ## max_voices: maximum number to pre-allocate and set the limit to 76 | 77 | proc tsf_set_max_voices*(f: ptr tsf; max_voices: cint) {.cdecl, importc.} 78 | ## Start playing a note 79 | ## preset_index: preset index >= 0 and < tsf_get_presetcount() 80 | ## key: note value between 0 and 127 (60 being middle C) 81 | ## vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) 82 | ## bank: instrument bank number (alternative to preset_index) 83 | ## preset_number: preset number (alternative to preset_index) 84 | ## (bank_note_on returns 0 if preset does not exist, otherwise 1) 85 | 86 | proc tsf_note_on*(f: ptr tsf; preset_index: cint; key: cint; vel: cfloat) {.cdecl, importc.} 87 | proc tsf_bank_note_on*(f: ptr tsf; bank: cint; preset_number: cint; key: cint; vel: cfloat): cint {.cdecl, importc.} 88 | ## Stop playing a note 89 | ## (bank_note_off returns 0 if preset does not exist, otherwise 1) 90 | 91 | proc tsf_note_off*(f: ptr tsf; preset_index: cint; key: cint) {.cdecl, importc.} 92 | proc tsf_bank_note_off*(f: ptr tsf; bank: cint; preset_number: cint; key: cint): cint {.cdecl, importc.} 93 | ## Stop playing all notes (end with sustain and release) 94 | 95 | proc tsf_note_off_all*(f: ptr tsf) {.cdecl, importc.} 96 | ## Returns the number of active voices 97 | 98 | proc tsf_active_voice_count*(f: ptr tsf): cint {.cdecl, importc.} 99 | ## Render output samples into a buffer 100 | ## You can either render as signed 16-bit values (tsf_render_short) or 101 | ## as 32-bit float values (tsf_render_float) 102 | ## buffer: target buffer of size samples * output_channels * sizeof(type) 103 | ## samples: number of samples to render 104 | ## flag_mixing: if 0 clear the buffer first, otherwise mix into existing data 105 | 106 | proc tsf_render_short*(f: ptr tsf; buffer: ptr cshort; samples: cint; flag_mixing: cint) {.cdecl, importc.} 107 | proc tsf_render_float*(f: ptr tsf; buffer: ptr cfloat; samples: cint; flag_mixing: cint) {.cdecl, importc.} 108 | ## Higher level channel based functions, set up channel parameters 109 | ## channel: channel number 110 | ## preset_index: preset index >= 0 and < tsf_get_presetcount() 111 | ## preset_number: preset number (alternative to preset_index) 112 | ## flag_mididrums: 0 for normal channels, otherwise apply MIDI drum channel rules 113 | ## bank: instrument bank number (alternative to preset_index) 114 | ## pan: stereo panning value from 0.0 (left) to 1.0 (right) (default 0.5 center) 115 | ## volume: linear volume scale factor (default 1.0 full) 116 | ## pitch_wheel: pitch wheel position 0 to 16383 (default 8192 unpitched) 117 | ## pitch_range: range of the pitch wheel in semitones (default 2.0, total +/- 2 semitones) 118 | ## tuning: tuning of all playing voices in semitones (default 0.0, standard (A440) tuning) 119 | ## (set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1) 120 | 121 | proc tsf_channel_set_presetindex*(f: ptr tsf; channel: cint; preset_index: cint) {.cdecl, importc.} 122 | proc tsf_channel_set_presetnumber*(f: ptr tsf; channel: cint; preset_number: cint; 123 | flag_mididrums: cint): cint {.cdecl, importc.} 124 | proc tsf_channel_set_bank*(f: ptr tsf; channel: cint; bank: cint) {.cdecl, importc.} 125 | proc tsf_channel_set_bank_preset*(f: ptr tsf; channel: cint; bank: cint; 126 | preset_number: cint): cint {.cdecl, importc.} 127 | proc tsf_channel_set_pan*(f: ptr tsf; channel: cint; pan: cfloat) {.cdecl, importc.} 128 | proc tsf_channel_set_volume*(f: ptr tsf; channel: cint; volume: cfloat) {.cdecl, importc.} 129 | proc tsf_channel_set_pitchwheel*(f: ptr tsf; channel: cint; pitch_wheel: cint) {.cdecl, importc.} 130 | proc tsf_channel_set_pitchrange*(f: ptr tsf; channel: cint; pitch_range: cfloat) {.cdecl, importc.} 131 | proc tsf_channel_set_tuning*(f: ptr tsf; channel: cint; tuning: cfloat) {.cdecl, importc.} 132 | ## Start or stop playing notes on a channel (needs channel preset to be set) 133 | ## channel: channel number 134 | ## key: note value between 0 and 127 (60 being middle C) 135 | ## vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) 136 | 137 | proc tsf_channel_note_on*(f: ptr tsf; channel: cint; key: cint; vel: cfloat) {.cdecl, importc.} 138 | proc tsf_channel_note_off*(f: ptr tsf; channel: cint; key: cint) {.cdecl, importc.} 139 | proc tsf_channel_note_off_all*(f: ptr tsf; channel: cint) {.cdecl, importc.} 140 | ## end with sustain and release 141 | 142 | proc tsf_channel_sounds_off_all*(f: ptr tsf; channel: cint) {.cdecl, importc.} 143 | ## end immediatly 144 | ## Apply a MIDI control change to the channel (not all controllers are supported!) 145 | 146 | proc tsf_channel_midi_control*(f: ptr tsf; channel: cint; controller: cint; 147 | control_value: cint) {.cdecl, importc.} 148 | ## Get current values set on the channels 149 | 150 | proc tsf_channel_get_preset_index*(f: ptr tsf; channel: cint): cint {.cdecl, importc.} 151 | proc tsf_channel_get_preset_bank*(f: ptr tsf; channel: cint): cint {.cdecl, importc.} 152 | proc tsf_channel_get_preset_number*(f: ptr tsf; channel: cint): cint {.cdecl, importc.} 153 | proc tsf_channel_get_pan*(f: ptr tsf; channel: cint): cfloat {.cdecl, importc.} 154 | proc tsf_channel_get_volume*(f: ptr tsf; channel: cint): cfloat {.cdecl, importc.} 155 | proc tsf_channel_get_pitchwheel*(f: ptr tsf; channel: cint): cint {.cdecl, importc.} 156 | proc tsf_channel_get_pitchrange*(f: ptr tsf; channel: cint): cfloat {.cdecl, importc.} 157 | proc tsf_channel_get_tuning*(f: ptr tsf; channel: cint): cfloat {.cdecl, importc.} 158 | -------------------------------------------------------------------------------- /src/paramidi.nim: -------------------------------------------------------------------------------- 1 | from sequtils import nil 2 | from algorithm import nil 3 | import paramidi/tsf 4 | from paramidi/constants import nil 5 | import json, sets 6 | 7 | type 8 | Note* = enum 9 | c, cx, d, dx, e, f, fx, g, gx, a, ax, b, 10 | r, # rest 11 | `c+`, `c+1`, `c+2`, `c+3`, `c+4`, `c+5`, `c+6`, 12 | `c-`, `c-1`, `c-2`, `c-3`, `c-4`, `c-5`, `c-6`, 13 | c1, c2, c3, c4, c5, c6, c7, 14 | `cx+`, `cx+1`, `cx+2`, `cx+3`, `cx+4`, `cx+5`, `cx+6`, 15 | `cx-`, `cx-1`, `cx-2`, `cx-3`, `cx-4`, `cx-5`, `cx-6`, 16 | cx1, cx2, cx3, cx4, cx5, cx6, cx7, 17 | `d+`, `d+1`, `d+2`, `d+3`, `d+4`, `d+5`, `d+6`, 18 | `d-`, `d-1`, `d-2`, `d-3`, `d-4`, `d-5`, `d-6`, 19 | d1, d2, d3, d4, d5, d6, d7, 20 | `dx+`, `dx+1`, `dx+2`, `dx+3`, `dx+4`, `dx+5`, `dx+6`, 21 | `dx-`, `dx-1`, `dx-2`, `dx-3`, `dx-4`, `dx-5`, `dx-6`, 22 | dx1, dx2, dx3, dx4, dx5, dx6, dx7, 23 | `e+`, `e+1`, `e+2`, `e+3`, `e+4`, `e+5`, `e+6`, 24 | `e-`, `e-1`, `e-2`, `e-3`, `e-4`, `e-5`, `e-6`, 25 | e1, e2, e3, e4, e5, e6, e7, 26 | `f+`, `f+1`, `f+2`, `f+3`, `f+4`, `f+5`, `f+6`, 27 | `f-`, `f-1`, `f-2`, `f-3`, `f-4`, `f-5`, `f-6`, 28 | f1, f2, f3, f4, f5, f6, f7, 29 | `fx+`, `fx+1`, `fx+2`, `fx+3`, `fx+4`, `fx+5`, `fx+6`, 30 | `fx-`, `fx-1`, `fx-2`, `fx-3`, `fx-4`, `fx-5`, `fx-6`, 31 | fx1, fx2, fx3, fx4, fx5, fx6, fx7, 32 | `g+`, `g+1`, `g+2`, `g+3`, `g+4`, `g+5`, `g+6`, 33 | `g-`, `g-1`, `g-2`, `g-3`, `g-4`, `g-5`, `g-6`, 34 | g1, g2, g3, g4, g5, g6, g7, 35 | `gx+`, `gx+1`, `gx+2`, `gx+3`, `gx+4`, `gx+5`, `gx+6`, 36 | `gx-`, `gx-1`, `gx-2`, `gx-3`, `gx-4`, `gx-5`, `gx-6`, 37 | gx1, gx2, gx3, gx4, gx5, gx6, gx7, 38 | `a+`, `a+1`, `a+2`, `a+3`, `a+4`, `a+5`, `a+6`, 39 | `a-`, `a-1`, `a-2`, `a-3`, `a-4`, `a-5`, `a-6`, 40 | a1, a2, a3, a4, a5, a6, a7, 41 | `ax+`, `ax+1`, `ax+2`, `ax+3`, `ax+4`, `ax+5`, `ax+6`, 42 | `ax-`, `ax-1`, `ax-2`, `ax-3`, `ax-4`, `ax-5`, `ax-6`, 43 | ax1, ax2, ax3, ax4, ax5, ax6, ax7, 44 | `b+`, `b+1`, `b+2`, `b+3`, `b+4`, `b+5`, `b+6`, 45 | `b-`, `b-1`, `b-2`, `b-3`, `b-4`, `b-5`, `b-6`, 46 | b1, b2, b3, b4, b5, b6, b7, 47 | Instrument* = enum 48 | none = -1, 49 | # Piano 50 | piano, # acoustic_grand_piano 51 | bright_acoustic_piano, 52 | electric_grand_piano, 53 | honky_tonk_piano, 54 | electric_piano_1, 55 | electric_piano_2, 56 | harpsichord, 57 | clavinet, 58 | # Chromatic Percussion 59 | celesta, 60 | glockenspiel, 61 | music_box, 62 | vibraphone, 63 | marimba, 64 | xylophone, 65 | tubular_bells, 66 | dulcimer, 67 | # Organ 68 | drawbar_organ, 69 | percussive_organ, 70 | rock_organ, 71 | organ, # church_organ 72 | reed_organ, 73 | accordion, 74 | harmonica, 75 | tango_accordion, 76 | # Guitar 77 | guitar, # acoustic_guitar_nylon 78 | acoustic_guitar_steel, 79 | electric_guitar_jazz, 80 | electric_guitar_clean, 81 | electric_guitar_palm_muted, 82 | electric_guitar_overdrive, 83 | electric_guitar_distorted, 84 | electric_guitar_harmonics, 85 | # Bass 86 | acoustic_bass, 87 | electric_bass, # electric_bass_finger 88 | electric_bass_pick, 89 | fretless_bass, 90 | bass_slap, 91 | bass_pop, 92 | synth_bass_1, 93 | synth_bass_2, 94 | # Strings 95 | violin, 96 | viola, 97 | cello, 98 | contrabass, 99 | tremolo_strings, 100 | pizzicato_strings, 101 | harp, # orchestral_harp 102 | timpani, 103 | # Ensemble 104 | string_ensemble_1, 105 | string_ensemble_2, 106 | synth_strings_1, 107 | synth_strings_2, 108 | choir_aahs, 109 | voice_oohs, 110 | synth_voice, 111 | orchestra_hit, 112 | # Brass 113 | trumpet, 114 | trombone, 115 | tuba, 116 | muted_trumpet, 117 | french_horn, 118 | brass_section, 119 | synth_brass_1, 120 | synth_brass_2, 121 | # Reed 122 | soprano_sax, 123 | alto_sax, 124 | tenor_sax, 125 | baritone_sax, 126 | oboe, 127 | english_horn, 128 | bassoon, 129 | clarinet, 130 | # Pipe 131 | piccolo, 132 | flute, 133 | recorder, 134 | pan_flute, 135 | bottle, 136 | shakuhachi, 137 | whistle, 138 | ocarina, 139 | # Synth Lead 140 | square_lead, 141 | saw_wave, 142 | calliope_lead, 143 | chiffer_lead, 144 | charang, 145 | solo_vox, 146 | fifths, 147 | bass_and_lead, 148 | # Synth Pad 149 | synth_pad_new_age, 150 | synth_pad_warm, 151 | synth_pad_polysynth, 152 | synth_pad_choir, 153 | synth_pad_bowed, 154 | synth_pad_metallic, 155 | synth_pad_halo, 156 | synth_pad_sweep, 157 | # Synth Effects 158 | fx_rain, 159 | fx_soundtrack, 160 | fx_crystal, 161 | fx_atmosphere, 162 | fx_brightness, 163 | fx_goblins, 164 | fx_echoes, 165 | fx_sci_fi, 166 | # Ethnic 167 | sitar, 168 | banjo, 169 | shamisen, 170 | koto, 171 | kalimba, 172 | bagpipes, 173 | fiddle, 174 | shehnai, 175 | # Percussive 176 | tinkle_bell, 177 | agogo, 178 | steel_drums, 179 | woodblock, 180 | taiko_drum, 181 | melodic_tom, 182 | synth_drum, 183 | reverse_cymbal, 184 | # Sound Effects 185 | guitar_fret_noise, 186 | breath_noise, 187 | seashore, 188 | bird_tweet, 189 | telephone_ring, 190 | helicopter, 191 | applause, 192 | gun_shot, 193 | RelativeOctave* = enum 194 | `+1`, `+2`, `+3`, `+4`, `+5`, `+6`, 195 | `-1`, `-2`, `-3`, `-4`, `-5`, `-6`, 196 | EventKind* = enum 197 | On, Off, 198 | Event* = object 199 | kind*: EventKind 200 | note*: Note 201 | time*: float 202 | instrument*: Instrument 203 | octave*: range[1..7] 204 | tempo*: int 205 | volume: int 206 | Mode* = enum 207 | sequential, concurrent, 208 | Context* = object 209 | events*: ref seq[Event] 210 | time*: float # total length of all notes 211 | seconds*: float # total number of seconds 212 | instrument*: Instrument 213 | octave*: range[1..7] 214 | tempo*: int 215 | length*: float 216 | play*: bool 217 | mode*: Mode 218 | volume: int 219 | RenderResult*[T] = object 220 | data*: seq[T] 221 | seconds*: float 222 | 223 | proc getNote(note: Note): Note = 224 | case note: 225 | of c, cx, d, dx, e, f, fx, g, gx, a, ax, b, r: 226 | note 227 | of `c+`, `c+1`, `c+2`, `c+3`, `c+4`, `c+5`, `c+6`, 228 | `c-`, `c-1`, `c-2`, `c-3`, `c-4`, `c-5`, `c-6`, 229 | c1, c2, c3, c4, c5, c6, c7: 230 | c 231 | of `cx+`, `cx+1`, `cx+2`, `cx+3`, `cx+4`, `cx+5`, `cx+6`, 232 | `cx-`, `cx-1`, `cx-2`, `cx-3`, `cx-4`, `cx-5`, `cx-6`, 233 | cx1, cx2, cx3, cx4, cx5, cx6, cx7: 234 | cx 235 | of `d+`, `d+1`, `d+2`, `d+3`, `d+4`, `d+5`, `d+6`, 236 | `d-`, `d-1`, `d-2`, `d-3`, `d-4`, `d-5`, `d-6`, 237 | d1, d2, d3, d4, d5, d6, d7: 238 | d 239 | of `dx+`, `dx+1`, `dx+2`, `dx+3`, `dx+4`, `dx+5`, `dx+6`, 240 | `dx-`, `dx-1`, `dx-2`, `dx-3`, `dx-4`, `dx-5`, `dx-6`, 241 | dx1, dx2, dx3, dx4, dx5, dx6, dx7: 242 | dx 243 | of `e+`, `e+1`, `e+2`, `e+3`, `e+4`, `e+5`, `e+6`, 244 | `e-`, `e-1`, `e-2`, `e-3`, `e-4`, `e-5`, `e-6`, 245 | e1, e2, e3, e4, e5, e6, e7: 246 | e 247 | of `f+`, `f+1`, `f+2`, `f+3`, `f+4`, `f+5`, `f+6`, 248 | `f-`, `f-1`, `f-2`, `f-3`, `f-4`, `f-5`, `f-6`, 249 | f1, f2, f3, f4, f5, f6, f7: 250 | f 251 | of `fx+`, `fx+1`, `fx+2`, `fx+3`, `fx+4`, `fx+5`, `fx+6`, 252 | `fx-`, `fx-1`, `fx-2`, `fx-3`, `fx-4`, `fx-5`, `fx-6`, 253 | fx1, fx2, fx3, fx4, fx5, fx6, fx7: 254 | fx 255 | of `g+`, `g+1`, `g+2`, `g+3`, `g+4`, `g+5`, `g+6`, 256 | `g-`, `g-1`, `g-2`, `g-3`, `g-4`, `g-5`, `g-6`, 257 | g1, g2, g3, g4, g5, g6, g7: 258 | g 259 | of `gx+`, `gx+1`, `gx+2`, `gx+3`, `gx+4`, `gx+5`, `gx+6`, 260 | `gx-`, `gx-1`, `gx-2`, `gx-3`, `gx-4`, `gx-5`, `gx-6`, 261 | gx1, gx2, gx3, gx4, gx5, gx6, gx7: 262 | gx 263 | of `a+`, `a+1`, `a+2`, `a+3`, `a+4`, `a+5`, `a+6`, 264 | `a-`, `a-1`, `a-2`, `a-3`, `a-4`, `a-5`, `a-6`, 265 | a1, a2, a3, a4, a5, a6, a7: 266 | a 267 | of `ax+`, `ax+1`, `ax+2`, `ax+3`, `ax+4`, `ax+5`, `ax+6`, 268 | `ax-`, `ax-1`, `ax-2`, `ax-3`, `ax-4`, `ax-5`, `ax-6`, 269 | ax1, ax2, ax3, ax4, ax5, ax6, ax7: 270 | ax 271 | of `b+`, `b+1`, `b+2`, `b+3`, `b+4`, `b+5`, `b+6`, 272 | `b-`, `b-1`, `b-2`, `b-3`, `b-4`, `b-5`, `b-6`, 273 | b1, b2, b3, b4, b5, b6, b7: 274 | b 275 | 276 | proc getOctave(ctx: Context, note: Note): range[1..7] = 277 | case note: 278 | of c, cx, d, dx, e, f, fx, g, gx, a, ax, b, r: 279 | ctx.octave 280 | of `c+`, `cx+`, `d+`, `dx+`, `e+`, `f+`, `fx+`, `g+`, `gx+`, `a+`, `ax+`, `b+`, 281 | `c+1`, `cx+1`, `d+1`, `dx+1`, `e+1`, `f+1`, `fx+1`, `g+1`, `gx+1`, `a+1`, `ax+1`, `b+1`: 282 | ctx.octave + 1 283 | of `c+2`, `cx+2`, `d+2`, `dx+2`, `e+2`, `f+2`, `fx+2`, `g+2`, `gx+2`, `a+2`, `ax+2`, `b+2`: 284 | ctx.octave + 2 285 | of `c+3`, `cx+3`, `d+3`, `dx+3`, `e+3`, `f+3`, `fx+3`, `g+3`, `gx+3`, `a+3`, `ax+3`, `b+3`: 286 | ctx.octave + 3 287 | of `c+4`, `cx+4`, `d+4`, `dx+4`, `e+4`, `f+4`, `fx+4`, `g+4`, `gx+4`, `a+4`, `ax+4`, `b+4`: 288 | ctx.octave + 4 289 | of `c+5`, `cx+5`, `d+5`, `dx+5`, `e+5`, `f+5`, `fx+5`, `g+5`, `gx+5`, `a+5`, `ax+5`, `b+5`: 290 | ctx.octave + 5 291 | of `c+6`, `cx+6`, `d+6`, `dx+6`, `e+6`, `f+6`, `fx+6`, `g+6`, `gx+6`, `a+6`, `ax+6`, `b+6`: 292 | ctx.octave + 6 293 | of `c-`, `cx-`, `d-`, `dx-`, `e-`, `f-`, `fx-`, `g-`, `gx-`, `a-`, `ax-`, `b-`, 294 | `c-1`, `cx-1`, `d-1`, `dx-1`, `e-1`, `f-1`, `fx-1`, `g-1`, `gx-1`, `a-1`, `ax-1`, `b-1`: 295 | ctx.octave - 1 296 | of `c-2`, `cx-2`, `d-2`, `dx-2`, `e-2`, `f-2`, `fx-2`, `g-2`, `gx-2`, `a-2`, `ax-2`, `b-2`: 297 | ctx.octave - 2 298 | of `c-3`, `cx-3`, `d-3`, `dx-3`, `e-3`, `f-3`, `fx-3`, `g-3`, `gx-3`, `a-3`, `ax-3`, `b-3`: 299 | ctx.octave - 3 300 | of `c-4`, `cx-4`, `d-4`, `dx-4`, `e-4`, `f-4`, `fx-4`, `g-4`, `gx-4`, `a-4`, `ax-4`, `b-4`: 301 | ctx.octave - 4 302 | of `c-5`, `cx-5`, `d-5`, `dx-5`, `e-5`, `f-5`, `fx-5`, `g-5`, `gx-5`, `a-5`, `ax-5`, `b-5`: 303 | ctx.octave - 5 304 | of `c-6`, `cx-6`, `d-6`, `dx-6`, `e-6`, `f-6`, `fx-6`, `g-6`, `gx-6`, `a-6`, `ax-6`, `b-6`: 305 | ctx.octave - 6 306 | of c1, cx1, d1, dx1, e1, f1, fx1, g1, gx1, a1, ax1, b1: 307 | 1 308 | of c2, cx2, d2, dx2, e2, f2, fx2, g2, gx2, a2, ax2, b2: 309 | 2 310 | of c3, cx3, d3, dx3, e3, f3, fx3, g3, gx3, a3, ax3, b3: 311 | 3 312 | of c4, cx4, d4, dx4, e4, f4, fx4, g4, gx4, a4, ax4, b4: 313 | 4 314 | of c5, cx5, d5, dx5, e5, f5, fx5, g5, gx5, a5, ax5, b5: 315 | 5 316 | of c6, cx6, d6, dx6, e6, f6, fx6, g6, gx6, a6, ax6, b6: 317 | 6 318 | of c7, cx7, d7, dx7, e7, f7, fx7, g7, gx7, a7, ax7, b7: 319 | 7 320 | 321 | proc getOctave(ctx: Context, relOctave: RelativeOctave): range[1..7] = 322 | case relOctave: 323 | of `+1`: ctx.octave + 1 324 | of `+2`: ctx.octave + 2 325 | of `+3`: ctx.octave + 3 326 | of `+4`: ctx.octave + 4 327 | of `+5`: ctx.octave + 5 328 | of `+6`: ctx.octave + 6 329 | of `-1`: ctx.octave - 1 330 | of `-2`: ctx.octave - 2 331 | of `-3`: ctx.octave - 3 332 | of `-4`: ctx.octave - 4 333 | of `-5`: ctx.octave - 5 334 | of `-6`: ctx.octave - 6 335 | 336 | proc `+`*(note: Note): Note = 337 | case note: 338 | of c: `c+` 339 | of cx: `cx+` 340 | of d: `d+` 341 | of dx: `dx+` 342 | of e: `e+` 343 | of f: `f+` 344 | of fx: `fx+` 345 | of g: `g+` 346 | of gx: `gx+` 347 | of a: `a+` 348 | of ax: `ax+` 349 | of b: `b+` 350 | else: 351 | raise newException(Exception, $ note & " note cannot use +") 352 | 353 | proc `-`*(note: Note): Note = 354 | case note: 355 | of c: `c-` 356 | of cx: `cx-` 357 | of d: `d-` 358 | of dx: `dx-` 359 | of e: `e-` 360 | of f: `f-` 361 | of fx: `fx-` 362 | of g: `g-` 363 | of gx: `gx-` 364 | of a: `a-` 365 | of ax: `ax-` 366 | of b: `b-` 367 | else: 368 | raise newException(Exception, $ note & " note cannot use -") 369 | 370 | proc `+`*(note: Note, octave: range[1..6]): Note = 371 | case note: 372 | of c: 373 | case octave: 374 | of 1: `c+1` 375 | of 2: `c+2` 376 | of 3: `c+3` 377 | of 4: `c+4` 378 | of 5: `c+5` 379 | of 6: `c+6` 380 | of cx: 381 | case octave: 382 | of 1: `cx+1` 383 | of 2: `cx+2` 384 | of 3: `cx+3` 385 | of 4: `cx+4` 386 | of 5: `cx+5` 387 | of 6: `cx+6` 388 | of d: 389 | case octave: 390 | of 1: `d+1` 391 | of 2: `d+2` 392 | of 3: `d+3` 393 | of 4: `d+4` 394 | of 5: `d+5` 395 | of 6: `d+6` 396 | of dx: 397 | case octave: 398 | of 1: `dx+1` 399 | of 2: `dx+2` 400 | of 3: `dx+3` 401 | of 4: `dx+4` 402 | of 5: `dx+5` 403 | of 6: `dx+6` 404 | of e: 405 | case octave: 406 | of 1: `e+1` 407 | of 2: `e+2` 408 | of 3: `e+3` 409 | of 4: `e+4` 410 | of 5: `e+5` 411 | of 6: `e+6` 412 | of f: 413 | case octave: 414 | of 1: `f+1` 415 | of 2: `f+2` 416 | of 3: `f+3` 417 | of 4: `f+4` 418 | of 5: `f+5` 419 | of 6: `f+6` 420 | of fx: 421 | case octave: 422 | of 1: `fx+1` 423 | of 2: `fx+2` 424 | of 3: `fx+3` 425 | of 4: `fx+4` 426 | of 5: `fx+5` 427 | of 6: `fx+6` 428 | of g: 429 | case octave: 430 | of 1: `g+1` 431 | of 2: `g+2` 432 | of 3: `g+3` 433 | of 4: `g+4` 434 | of 5: `g+5` 435 | of 6: `g+6` 436 | of gx: 437 | case octave: 438 | of 1: `gx+1` 439 | of 2: `gx+2` 440 | of 3: `gx+3` 441 | of 4: `gx+4` 442 | of 5: `gx+5` 443 | of 6: `gx+6` 444 | of a: 445 | case octave: 446 | of 1: `a+1` 447 | of 2: `a+2` 448 | of 3: `a+3` 449 | of 4: `a+4` 450 | of 5: `a+5` 451 | of 6: `a+6` 452 | of ax: 453 | case octave: 454 | of 1: `ax+1` 455 | of 2: `ax+2` 456 | of 3: `ax+3` 457 | of 4: `ax+4` 458 | of 5: `ax+5` 459 | of 6: `ax+6` 460 | of b: 461 | case octave: 462 | of 1: `b+1` 463 | of 2: `b+2` 464 | of 3: `b+3` 465 | of 4: `b+4` 466 | of 5: `b+5` 467 | of 6: `b+6` 468 | else: 469 | raise newException(Exception, $ note & " note cannot use +") 470 | 471 | proc `-`*(note: Note, octave: range[1..6]): Note = 472 | case note: 473 | of c: 474 | case octave: 475 | of 1: `c-1` 476 | of 2: `c-2` 477 | of 3: `c-3` 478 | of 4: `c-4` 479 | of 5: `c-5` 480 | of 6: `c-6` 481 | of cx: 482 | case octave: 483 | of 1: `cx-1` 484 | of 2: `cx-2` 485 | of 3: `cx-3` 486 | of 4: `cx-4` 487 | of 5: `cx-5` 488 | of 6: `cx-6` 489 | of d: 490 | case octave: 491 | of 1: `d-1` 492 | of 2: `d-2` 493 | of 3: `d-3` 494 | of 4: `d-4` 495 | of 5: `d-5` 496 | of 6: `d-6` 497 | of dx: 498 | case octave: 499 | of 1: `dx-1` 500 | of 2: `dx-2` 501 | of 3: `dx-3` 502 | of 4: `dx-4` 503 | of 5: `dx-5` 504 | of 6: `dx-6` 505 | of e: 506 | case octave: 507 | of 1: `e-1` 508 | of 2: `e-2` 509 | of 3: `e-3` 510 | of 4: `e-4` 511 | of 5: `e-5` 512 | of 6: `e-6` 513 | of f: 514 | case octave: 515 | of 1: `f-1` 516 | of 2: `f-2` 517 | of 3: `f-3` 518 | of 4: `f-4` 519 | of 5: `f-5` 520 | of 6: `f-6` 521 | of fx: 522 | case octave: 523 | of 1: `fx-1` 524 | of 2: `fx-2` 525 | of 3: `fx-3` 526 | of 4: `fx-4` 527 | of 5: `fx-5` 528 | of 6: `fx-6` 529 | of g: 530 | case octave: 531 | of 1: `g-1` 532 | of 2: `g-2` 533 | of 3: `g-3` 534 | of 4: `g-4` 535 | of 5: `g-5` 536 | of 6: `g-6` 537 | of gx: 538 | case octave: 539 | of 1: `gx-1` 540 | of 2: `gx-2` 541 | of 3: `gx-3` 542 | of 4: `gx-4` 543 | of 5: `gx-5` 544 | of 6: `gx-6` 545 | of a: 546 | case octave: 547 | of 1: `a-1` 548 | of 2: `a-2` 549 | of 3: `a-3` 550 | of 4: `a-4` 551 | of 5: `a-5` 552 | of 6: `a-6` 553 | of ax: 554 | case octave: 555 | of 1: `ax-1` 556 | of 2: `ax-2` 557 | of 3: `ax-3` 558 | of 4: `ax-4` 559 | of 5: `ax-5` 560 | of 6: `ax-6` 561 | of b: 562 | case octave: 563 | of 1: `b-1` 564 | of 2: `b-2` 565 | of 3: `b-3` 566 | of 4: `b-4` 567 | of 5: `b-5` 568 | of 6: `b-6` 569 | else: 570 | raise newException(Exception, $ note & " note cannot use -") 571 | 572 | proc compileContent(ctx: var Context, note: Note) = 573 | if not ctx.play: 574 | return 575 | if ctx.instrument == none and note != r: 576 | raise newException(Exception, $ note & " note cannot be played without an instrument") 577 | let 578 | realNote = getNote(note) 579 | octave = getOctave(ctx, note) 580 | ctx.events[].add(Event( 581 | kind: On, 582 | note: realNote, 583 | time: ctx.time, 584 | instrument: ctx.instrument, 585 | octave: octave, 586 | tempo: ctx.tempo, 587 | volume: ctx.volume 588 | )) 589 | ctx.events[].add(Event( 590 | kind: Off, 591 | note: realNote, 592 | time: ctx.time + ctx.length, 593 | instrument: ctx.instrument, 594 | octave: octave, 595 | tempo: ctx.tempo, 596 | volume: ctx.volume 597 | )) 598 | ctx.time += ctx.length 599 | 600 | proc compileContent(ctx: var Context, chord: set[Note]) = 601 | if not ctx.play: 602 | return 603 | let time = ctx.time 604 | for note in chord: 605 | compileContent(ctx, note) 606 | ctx.time = time 607 | ctx.time += ctx.length 608 | 609 | proc compileContent(ctx: var Context, instrument: Instrument) = 610 | ctx.instrument = instrument 611 | 612 | proc setLength(ctx: var Context, length: float) = 613 | ctx.length = length 614 | 615 | proc setLength(ctx: var Context, length: int) = 616 | ctx.length = length.float 617 | 618 | proc setLength(ctx: var Context, length: BiggestInt) = 619 | ctx.length = length.float 620 | 621 | proc compileContent(ctx: var Context, length: float | int | BiggestInt) = 622 | setLength(ctx, length) 623 | 624 | proc compileContent(ctx: var Context, content: tuple) = 625 | var 626 | temp = ctx 627 | concurrent = false 628 | longestTime = ctx.time 629 | for k, v in content.fieldPairs: 630 | when k == "length": 631 | setLength(ctx, v) 632 | elif k == "octave": 633 | when v is RelativeOctave: 634 | ctx.octave = getOctave(ctx, v) 635 | else: 636 | ctx.octave = v 637 | elif k == "play": 638 | ctx.play = v 639 | elif k == "mode": 640 | ctx.mode = v 641 | elif k == "tempo": 642 | ctx.tempo = v 643 | elif k == "volume": 644 | ctx.volume = v 645 | else: 646 | let mode = temp.mode 647 | compileContent(temp, v) 648 | if mode != temp.mode and temp.mode == Mode.concurrent: 649 | concurrent = true 650 | temp.mode = Mode.sequential 651 | if concurrent: 652 | if temp.time > longestTime: 653 | longestTime = temp.time 654 | temp.time = ctx.time 655 | if concurrent: 656 | ctx.time = longestTime 657 | else: 658 | ctx.time = temp.time 659 | 660 | proc compileContent(ctx: var Context, node: JsonNode) = 661 | case node.kind: 662 | of JString: 663 | if constants.noteSet.contains(node.str): 664 | compileContent(ctx, Note(constants.notes.find(node.str))) 665 | elif constants.instruments.contains(node.str): 666 | compileContent(ctx, Instrument(constants.instruments.find(node.str))) 667 | else: 668 | raise newException(Exception, "Invalid value: " & $node & " (expected a note or instrument)") 669 | of JInt: 670 | compileContent(ctx, node.num) 671 | of JFloat: 672 | compileContent(ctx, node.fnum) 673 | of JBool, JNull: 674 | raise newException(Exception, "Invalid value: " & $node) 675 | of JObject: 676 | for k, v in node: 677 | if k == "length": 678 | case v.kind: 679 | of JInt: 680 | setLength(ctx, v.num) 681 | of JFloat: 682 | setLength(ctx, v.fnum) 683 | else: 684 | raise newException(Exception, "Invalid length: " & $v) 685 | elif k == "octave": 686 | if v.kind == JInt: 687 | ctx.octave = v.num 688 | elif v.kind == JString: 689 | if constants.relativeOctaveSet.contains(v.str): 690 | ctx.octave = getOctave(ctx, RelativeOctave(constants.relativeOctaves.find(v.str))) 691 | else: 692 | raise newException(Exception, "Invalid relative octave: " & $v) 693 | else: 694 | raise newException(Exception, "Invalid octave: " & $v) 695 | elif k == "play": 696 | if v.kind == JBool: 697 | ctx.play = v.bval 698 | else: 699 | raise newException(Exception, "Invalid play: " & $v) 700 | elif k == "mode": 701 | if v.kind == JString and constants.modeSet.contains(v.str): 702 | ctx.mode = Mode(constants.modes.find(v.str)) 703 | else: 704 | raise newException(Exception, "Invalid mode: " & $v) 705 | elif k == "tempo": 706 | if v.kind == JInt: 707 | ctx.tempo = v.num.int 708 | else: 709 | raise newException(Exception, "Invalid tempo: " & $v) 710 | elif k == "volume": 711 | if v.kind == JInt: 712 | ctx.volume = v.num.int 713 | else: 714 | raise newException(Exception, "Invalid volume: " & $v) 715 | else: 716 | raise newException(Exception, "Invalid attribute: " & k) 717 | of JArray: 718 | var 719 | temp = ctx 720 | concurrent = false 721 | longestTime = ctx.time 722 | for item in node: 723 | let mode = temp.mode 724 | compileContent(temp, item) 725 | if mode != temp.mode and temp.mode == Mode.concurrent: 726 | concurrent = true 727 | temp.mode = Mode.sequential 728 | if concurrent: 729 | if temp.time > longestTime: 730 | longestTime = temp.time 731 | temp.time = ctx.time 732 | if concurrent: 733 | ctx.time = longestTime 734 | else: 735 | ctx.time = temp.time 736 | 737 | proc initContext*(): Context = 738 | result = Context( 739 | time: 0, 740 | instrument: none, 741 | octave: 4, 742 | length: 1/4, 743 | play: true, 744 | mode: sequential, 745 | tempo: 120, 746 | volume: 100 747 | ) 748 | new result.events 749 | 750 | proc compile*(ctx: var Context, content: tuple | JsonNode): seq[Event] = 751 | compileContent(ctx, content) 752 | result = ctx.events[] 753 | algorithm.sort(result, proc (x, y: Event): int = 754 | if x.time < y.time: 755 | -1 756 | elif x.time > y.time: 757 | 1 758 | else: 759 | 0 760 | ) 761 | # calculate the total time in seconds 762 | var lastTime = 0.0 763 | ctx.seconds = 0 764 | const 765 | minuteSecs = 60 766 | quarterNote = 1/4 767 | for event in result: 768 | case event.kind: 769 | of On: 770 | discard 771 | of Off: 772 | let 773 | noteLength = event.time - lastTime 774 | noteLengthSeconds = (minuteSecs / event.tempo) * (noteLength / quarterNote) 775 | lastTime = event.time 776 | ctx.seconds += noteLengthSeconds 777 | 778 | proc compile*(content: tuple | JsonNode): seq[Event] = 779 | var ctx = initContext() 780 | compile(ctx, content) 781 | 782 | proc render*[T: cshort](events: seq[Event], soundFont: ptr tsf, sampleRate: int): RenderResult[T] = 783 | var lastTime = 0.0 784 | const 785 | scaleCount = 12 786 | minuteSecs = 60 787 | quarterNote = 1/4 788 | for event in events: 789 | let note = cint(event.note.ord + event.octave.ord * scaleCount + scaleCount) 790 | tsf_set_volume(soundFont, cfloat event.volume / 100) 791 | case event.kind: 792 | of On: 793 | if event.note != r: 794 | tsf_note_on(soundFont, event.instrument.ord.cint, note, 1.0f) 795 | of Off: 796 | let 797 | currentSize = result.data.len 798 | noteLength = event.time - lastTime 799 | noteLengthSeconds = (minuteSecs / event.tempo) * (noteLength / quarterNote) 800 | numSamples = sampleRate.float * noteLengthSeconds 801 | newSize = currentSize + numSamples.int 802 | if noteLength > 0: 803 | result.data.setLen(newSize) 804 | when T is cshort: 805 | tsf_render_short(soundFont, result.data[currentSize].addr, numSamples.cint, 0) 806 | if event.note != r: 807 | tsf_note_off(soundFont, event.instrument.ord.cint, note) 808 | lastTime = event.time 809 | result.seconds += noteLengthSeconds 810 | -------------------------------------------------------------------------------- /src/paramidi/tsf.h: -------------------------------------------------------------------------------- 1 | /* TinySoundFont - v0.9 - SoundFont2 synthesizer - https://github.com/schellingb/TinySoundFont 2 | no warranty implied; use at your own risk 3 | Do this: 4 | #define TSF_IMPLEMENTATION 5 | before you include this file in *one* C or C++ file to create the implementation. 6 | // i.e. it should look like this: 7 | #include ... 8 | #include ... 9 | #define TSF_IMPLEMENTATION 10 | #include "tsf.h" 11 | 12 | [OPTIONAL] #define TSF_NO_STDIO to remove stdio dependency 13 | [OPTIONAL] #define TSF_MALLOC, TSF_REALLOC, and TSF_FREE to avoid stdlib.h 14 | [OPTIONAL] #define TSF_MEMCPY, TSF_MEMSET to avoid string.h 15 | [OPTIONAL] #define TSF_POW, TSF_POWF, TSF_EXPF, TSF_LOG, TSF_TAN, TSF_LOG10, TSF_SQRT to avoid math.h 16 | 17 | NOT YET IMPLEMENTED 18 | - Support for ChorusEffectsSend and ReverbEffectsSend generators 19 | - Better low-pass filter without lowering performance too much 20 | - Support for modulators 21 | 22 | LICENSE (MIT) 23 | 24 | Copyright (C) 2017, 2018 Bernhard Schelling 25 | Based on SFZero, Copyright (C) 2012 Steve Folta (https://github.com/stevefolta/SFZero) 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 28 | software and associated documentation files (the "Software"), to deal in the Software 29 | without restriction, including without limitation the rights to use, copy, modify, merge, 30 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 31 | to whom the Software is furnished to do so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included in all 34 | copies or substantial portions of the Software. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 37 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 38 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 39 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 40 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 41 | USE OR OTHER DEALINGS IN THE SOFTWARE. 42 | 43 | */ 44 | 45 | #ifndef TSF_INCLUDE_TSF_INL 46 | #define TSF_INCLUDE_TSF_INL 47 | 48 | #ifdef __cplusplus 49 | extern "C" { 50 | # define CPP_DEFAULT0 = 0 51 | #else 52 | # define CPP_DEFAULT0 53 | #endif 54 | 55 | //define this if you want the API functions to be static 56 | #ifdef TSF_STATIC 57 | #define TSFDEF static 58 | #else 59 | #define TSFDEF extern 60 | #endif 61 | 62 | // The load functions will return a pointer to a struct tsf which all functions 63 | // thereafter take as the first parameter. 64 | // On error the tsf_load* functions will return NULL most likely due to invalid 65 | // data (or if the file did not exist in tsf_load_filename). 66 | typedef struct tsf tsf; 67 | 68 | #ifndef TSF_NO_STDIO 69 | // Directly load a SoundFont from a .sf2 file path 70 | TSFDEF tsf* tsf_load_filename(const char* filename); 71 | #endif 72 | 73 | // Load a SoundFont from a block of memory 74 | TSFDEF tsf* tsf_load_memory(const void* buffer, int size); 75 | 76 | // Stream structure for the generic loading 77 | struct tsf_stream 78 | { 79 | // Custom data given to the functions as the first parameter 80 | void* data; 81 | 82 | // Function pointer will be called to read 'size' bytes into ptr (returns number of read bytes) 83 | int (*read)(void* data, void* ptr, unsigned int size); 84 | 85 | // Function pointer will be called to skip ahead over 'count' bytes (returns 1 on success, 0 on error) 86 | int (*skip)(void* data, unsigned int count); 87 | }; 88 | 89 | // Generic SoundFont loading method using the stream structure above 90 | TSFDEF tsf* tsf_load(struct tsf_stream* stream); 91 | 92 | // Free the memory related to this tsf instance 93 | TSFDEF void tsf_close(tsf* f); 94 | 95 | // Stop all playing notes immediatly and reset all channel parameters 96 | TSFDEF void tsf_reset(tsf* f); 97 | 98 | // Returns the preset index from a bank and preset number, or -1 if it does not exist in the loaded SoundFont 99 | TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number); 100 | 101 | // Returns the number of presets in the loaded SoundFont 102 | TSFDEF int tsf_get_presetcount(const tsf* f); 103 | 104 | // Returns the name of a preset index >= 0 and < tsf_get_presetcount() 105 | TSFDEF const char* tsf_get_presetname(const tsf* f, int preset_index); 106 | 107 | // Returns the name of a preset by bank and preset number 108 | TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number); 109 | 110 | // Supported output modes by the render methods 111 | enum TSFOutputMode 112 | { 113 | // Two channels with single left/right samples one after another 114 | TSF_STEREO_INTERLEAVED, 115 | // Two channels with all samples for the left channel first then right 116 | TSF_STEREO_UNWEAVED, 117 | // A single channel (stereo instruments are mixed into center) 118 | TSF_MONO, 119 | }; 120 | 121 | // Thread safety: 122 | // Your audio output which calls the tsf_render* functions will most likely 123 | // run on a different thread than where the playback tsf_note* functions 124 | // are called. In which case some sort of concurrency control like a 125 | // mutex needs to be used so they are not called at the same time. 126 | // Alternatively, you can pre-allocate a maximum number of voices that can 127 | // play simultaneously by calling tsf_set_max_voices after loading. 128 | // That way memory re-allocation will not happen during tsf_note_on and 129 | // TSF should become mostly thread safe. 130 | // There is a theoretical chance that ending notes would negatively influence 131 | // a voice that is rendering at the time but it is hard to say. 132 | // Also be aware, this has not been tested much. 133 | 134 | // Setup the parameters for the voice render methods 135 | // outputmode: if mono or stereo and how stereo channel data is ordered 136 | // samplerate: the number of samples per second (output frequency) 137 | // global_gain_db: volume gain in decibels (>0 means higher, <0 means lower) 138 | TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db CPP_DEFAULT0); 139 | 140 | // Set the global gain as a volume factor 141 | // global_gain: the desired volume where 1.0 is 100% 142 | TSFDEF void tsf_set_volume(tsf* f, float global_gain); 143 | 144 | // Set the maximum number of voices to play simultaneously 145 | // Depending on the soundfond, one note can cause many new voices to be started, 146 | // so don't keep this number too low or otherwise sounds may not play. 147 | // max_voices: maximum number to pre-allocate and set the limit to 148 | TSFDEF void tsf_set_max_voices(tsf* f, int max_voices); 149 | 150 | // Start playing a note 151 | // preset_index: preset index >= 0 and < tsf_get_presetcount() 152 | // key: note value between 0 and 127 (60 being middle C) 153 | // vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) 154 | // bank: instrument bank number (alternative to preset_index) 155 | // preset_number: preset number (alternative to preset_index) 156 | // (bank_note_on returns 0 if preset does not exist, otherwise 1) 157 | TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel); 158 | TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel); 159 | 160 | // Stop playing a note 161 | // (bank_note_off returns 0 if preset does not exist, otherwise 1) 162 | TSFDEF void tsf_note_off(tsf* f, int preset_index, int key); 163 | TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key); 164 | 165 | // Stop playing all notes (end with sustain and release) 166 | TSFDEF void tsf_note_off_all(tsf* f); 167 | 168 | // Returns the number of active voices 169 | TSFDEF int tsf_active_voice_count(tsf* f); 170 | 171 | // Render output samples into a buffer 172 | // You can either render as signed 16-bit values (tsf_render_short) or 173 | // as 32-bit float values (tsf_render_float) 174 | // buffer: target buffer of size samples * output_channels * sizeof(type) 175 | // samples: number of samples to render 176 | // flag_mixing: if 0 clear the buffer first, otherwise mix into existing data 177 | TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing CPP_DEFAULT0); 178 | TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing CPP_DEFAULT0); 179 | 180 | // Higher level channel based functions, set up channel parameters 181 | // channel: channel number 182 | // preset_index: preset index >= 0 and < tsf_get_presetcount() 183 | // preset_number: preset number (alternative to preset_index) 184 | // flag_mididrums: 0 for normal channels, otherwise apply MIDI drum channel rules 185 | // bank: instrument bank number (alternative to preset_index) 186 | // pan: stereo panning value from 0.0 (left) to 1.0 (right) (default 0.5 center) 187 | // volume: linear volume scale factor (default 1.0 full) 188 | // pitch_wheel: pitch wheel position 0 to 16383 (default 8192 unpitched) 189 | // pitch_range: range of the pitch wheel in semitones (default 2.0, total +/- 2 semitones) 190 | // tuning: tuning of all playing voices in semitones (default 0.0, standard (A440) tuning) 191 | // (set_preset_number and set_bank_preset return 0 if preset does not exist, otherwise 1) 192 | TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index); 193 | TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums CPP_DEFAULT0); 194 | TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank); 195 | TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number); 196 | TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan); 197 | TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume); 198 | TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel); 199 | TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range); 200 | TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning); 201 | 202 | // Start or stop playing notes on a channel (needs channel preset to be set) 203 | // channel: channel number 204 | // key: note value between 0 and 127 (60 being middle C) 205 | // vel: velocity as a float between 0.0 (equal to note off) and 1.0 (full) 206 | TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel); 207 | TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key); 208 | TSFDEF void tsf_channel_note_off_all(tsf* f, int channel); //end with sustain and release 209 | TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel); //end immediatly 210 | 211 | // Apply a MIDI control change to the channel (not all controllers are supported!) 212 | TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value); 213 | 214 | // Get current values set on the channels 215 | TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel); 216 | TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel); 217 | TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel); 218 | TSFDEF float tsf_channel_get_pan(tsf* f, int channel); 219 | TSFDEF float tsf_channel_get_volume(tsf* f, int channel); 220 | TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel); 221 | TSFDEF float tsf_channel_get_pitchrange(tsf* f, int channel); 222 | TSFDEF float tsf_channel_get_tuning(tsf* f, int channel); 223 | 224 | #ifdef __cplusplus 225 | # undef CPP_DEFAULT0 226 | } 227 | #endif 228 | 229 | // end header 230 | // --------------------------------------------------------------------------------------------------------- 231 | #endif //TSF_INCLUDE_TSF_INL 232 | 233 | #ifdef TSF_IMPLEMENTATION 234 | #undef TSF_IMPLEMENTATION 235 | 236 | // The lower this block size is the more accurate the effects are. 237 | // Increasing the value significantly lowers the CPU usage of the voice rendering. 238 | // If LFO affects the low-pass filter it can be hearable even as low as 8. 239 | #ifndef TSF_RENDER_EFFECTSAMPLEBLOCK 240 | #define TSF_RENDER_EFFECTSAMPLEBLOCK 64 241 | #endif 242 | 243 | // Grace release time for quick voice off (avoid clicking noise) 244 | #define TSF_FASTRELEASETIME 0.01f 245 | 246 | #if !defined(TSF_MALLOC) || !defined(TSF_FREE) || !defined(TSF_REALLOC) 247 | # include 248 | # define TSF_MALLOC malloc 249 | # define TSF_FREE free 250 | # define TSF_REALLOC realloc 251 | #endif 252 | 253 | #if !defined(TSF_MEMCPY) || !defined(TSF_MEMSET) 254 | # include 255 | # define TSF_MEMCPY memcpy 256 | # define TSF_MEMSET memset 257 | #endif 258 | 259 | #if !defined(TSF_POW) || !defined(TSF_POWF) || !defined(TSF_EXPF) || !defined(TSF_LOG) || !defined(TSF_TAN) || !defined(TSF_LOG10) || !defined(TSF_SQRT) 260 | # include 261 | # if !defined(__cplusplus) && !defined(NAN) && !defined(powf) && !defined(expf) && !defined(sqrtf) 262 | # define powf (float)pow // deal with old math.h 263 | # define expf (float)exp // files that come without 264 | # define sqrtf (float)sqrt // powf, expf and sqrtf 265 | # endif 266 | # define TSF_POW pow 267 | # define TSF_POWF powf 268 | # define TSF_EXPF expf 269 | # define TSF_LOG log 270 | # define TSF_TAN tan 271 | # define TSF_LOG10 log10 272 | # define TSF_SQRTF sqrtf 273 | #endif 274 | 275 | #ifndef TSF_NO_STDIO 276 | # include 277 | #endif 278 | 279 | #define TSF_TRUE 1 280 | #define TSF_FALSE 0 281 | #define TSF_BOOL char 282 | #define TSF_PI 3.14159265358979323846264338327950288 283 | #define TSF_NULL 0 284 | 285 | #ifdef __cplusplus 286 | extern "C" { 287 | #endif 288 | 289 | typedef char tsf_fourcc[4]; 290 | typedef signed char tsf_s8; 291 | typedef unsigned char tsf_u8; 292 | typedef unsigned short tsf_u16; 293 | typedef signed short tsf_s16; 294 | typedef unsigned int tsf_u32; 295 | typedef char tsf_char20[20]; 296 | 297 | #define TSF_FourCCEquals(value1, value2) (value1[0] == value2[0] && value1[1] == value2[1] && value1[2] == value2[2] && value1[3] == value2[3]) 298 | 299 | struct tsf 300 | { 301 | struct tsf_preset* presets; 302 | float* fontSamples; 303 | struct tsf_voice* voices; 304 | struct tsf_channels* channels; 305 | float* outputSamples; 306 | 307 | int presetNum; 308 | int voiceNum; 309 | int maxVoiceNum; 310 | int outputSampleSize; 311 | unsigned int voicePlayIndex; 312 | 313 | enum TSFOutputMode outputmode; 314 | float outSampleRate; 315 | float globalGainDB; 316 | }; 317 | 318 | #ifndef TSF_NO_STDIO 319 | static int tsf_stream_stdio_read(FILE* f, void* ptr, unsigned int size) { return (int)fread(ptr, 1, size, f); } 320 | static int tsf_stream_stdio_skip(FILE* f, unsigned int count) { return !fseek(f, count, SEEK_CUR); } 321 | TSFDEF tsf* tsf_load_filename(const char* filename) 322 | { 323 | tsf* res; 324 | struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_stdio_read, (int(*)(void*,unsigned int))&tsf_stream_stdio_skip }; 325 | #if __STDC_WANT_SECURE_LIB__ 326 | FILE* f = TSF_NULL; fopen_s(&f, filename, "rb"); 327 | #else 328 | FILE* f = fopen(filename, "rb"); 329 | #endif 330 | if (!f) 331 | { 332 | //if (e) *e = TSF_FILENOTFOUND; 333 | return TSF_NULL; 334 | } 335 | stream.data = f; 336 | res = tsf_load(&stream); 337 | fclose(f); 338 | return res; 339 | } 340 | #endif 341 | 342 | struct tsf_stream_memory { const char* buffer; unsigned int total, pos; }; 343 | static int tsf_stream_memory_read(struct tsf_stream_memory* m, void* ptr, unsigned int size) { if (size > m->total - m->pos) size = m->total - m->pos; TSF_MEMCPY(ptr, m->buffer+m->pos, size); m->pos += size; return size; } 344 | static int tsf_stream_memory_skip(struct tsf_stream_memory* m, unsigned int count) { if (m->pos + count > m->total) return 0; m->pos += count; return 1; } 345 | TSFDEF tsf* tsf_load_memory(const void* buffer, int size) 346 | { 347 | struct tsf_stream stream = { TSF_NULL, (int(*)(void*,void*,unsigned int))&tsf_stream_memory_read, (int(*)(void*,unsigned int))&tsf_stream_memory_skip }; 348 | struct tsf_stream_memory f = { 0, 0, 0 }; 349 | f.buffer = (const char*)buffer; 350 | f.total = size; 351 | stream.data = &f; 352 | return tsf_load(&stream); 353 | } 354 | 355 | enum { TSF_LOOPMODE_NONE, TSF_LOOPMODE_CONTINUOUS, TSF_LOOPMODE_SUSTAIN }; 356 | 357 | enum { TSF_SEGMENT_NONE, TSF_SEGMENT_DELAY, TSF_SEGMENT_ATTACK, TSF_SEGMENT_HOLD, TSF_SEGMENT_DECAY, TSF_SEGMENT_SUSTAIN, TSF_SEGMENT_RELEASE, TSF_SEGMENT_DONE }; 358 | 359 | struct tsf_hydra 360 | { 361 | struct tsf_hydra_phdr *phdrs; struct tsf_hydra_pbag *pbags; struct tsf_hydra_pmod *pmods; 362 | struct tsf_hydra_pgen *pgens; struct tsf_hydra_inst *insts; struct tsf_hydra_ibag *ibags; 363 | struct tsf_hydra_imod *imods; struct tsf_hydra_igen *igens; struct tsf_hydra_shdr *shdrs; 364 | int phdrNum, pbagNum, pmodNum, pgenNum, instNum, ibagNum, imodNum, igenNum, shdrNum; 365 | }; 366 | 367 | union tsf_hydra_genamount { struct { tsf_u8 lo, hi; } range; tsf_s16 shortAmount; tsf_u16 wordAmount; }; 368 | struct tsf_hydra_phdr { tsf_char20 presetName; tsf_u16 preset, bank, presetBagNdx; tsf_u32 library, genre, morphology; }; 369 | struct tsf_hydra_pbag { tsf_u16 genNdx, modNdx; }; 370 | struct tsf_hydra_pmod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; 371 | struct tsf_hydra_pgen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; 372 | struct tsf_hydra_inst { tsf_char20 instName; tsf_u16 instBagNdx; }; 373 | struct tsf_hydra_ibag { tsf_u16 instGenNdx, instModNdx; }; 374 | struct tsf_hydra_imod { tsf_u16 modSrcOper, modDestOper; tsf_s16 modAmount; tsf_u16 modAmtSrcOper, modTransOper; }; 375 | struct tsf_hydra_igen { tsf_u16 genOper; union tsf_hydra_genamount genAmount; }; 376 | struct tsf_hydra_shdr { tsf_char20 sampleName; tsf_u32 start, end, startLoop, endLoop, sampleRate; tsf_u8 originalPitch; tsf_s8 pitchCorrection; tsf_u16 sampleLink, sampleType; }; 377 | 378 | #define TSFR(FIELD) stream->read(stream->data, &i->FIELD, sizeof(i->FIELD)); 379 | static void tsf_hydra_read_phdr(struct tsf_hydra_phdr* i, struct tsf_stream* stream) { TSFR(presetName) TSFR(preset) TSFR(bank) TSFR(presetBagNdx) TSFR(library) TSFR(genre) TSFR(morphology) } 380 | static void tsf_hydra_read_pbag(struct tsf_hydra_pbag* i, struct tsf_stream* stream) { TSFR(genNdx) TSFR(modNdx) } 381 | static void tsf_hydra_read_pmod(struct tsf_hydra_pmod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } 382 | static void tsf_hydra_read_pgen(struct tsf_hydra_pgen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } 383 | static void tsf_hydra_read_inst(struct tsf_hydra_inst* i, struct tsf_stream* stream) { TSFR(instName) TSFR(instBagNdx) } 384 | static void tsf_hydra_read_ibag(struct tsf_hydra_ibag* i, struct tsf_stream* stream) { TSFR(instGenNdx) TSFR(instModNdx) } 385 | static void tsf_hydra_read_imod(struct tsf_hydra_imod* i, struct tsf_stream* stream) { TSFR(modSrcOper) TSFR(modDestOper) TSFR(modAmount) TSFR(modAmtSrcOper) TSFR(modTransOper) } 386 | static void tsf_hydra_read_igen(struct tsf_hydra_igen* i, struct tsf_stream* stream) { TSFR(genOper) TSFR(genAmount) } 387 | static void tsf_hydra_read_shdr(struct tsf_hydra_shdr* i, struct tsf_stream* stream) { TSFR(sampleName) TSFR(start) TSFR(end) TSFR(startLoop) TSFR(endLoop) TSFR(sampleRate) TSFR(originalPitch) TSFR(pitchCorrection) TSFR(sampleLink) TSFR(sampleType) } 388 | #undef TSFR 389 | 390 | struct tsf_riffchunk { tsf_fourcc id; tsf_u32 size; }; 391 | struct tsf_envelope { float delay, attack, hold, decay, sustain, release, keynumToHold, keynumToDecay; }; 392 | struct tsf_voice_envelope { float level, slope; int samplesUntilNextSegment; short segment, midiVelocity; struct tsf_envelope parameters; TSF_BOOL segmentIsExponential, isAmpEnv; }; 393 | struct tsf_voice_lowpass { double QInv, a0, a1, b1, b2, z1, z2; TSF_BOOL active; }; 394 | struct tsf_voice_lfo { int samplesUntil; float level, delta; }; 395 | 396 | struct tsf_region 397 | { 398 | int loop_mode; 399 | unsigned int sample_rate; 400 | unsigned char lokey, hikey, lovel, hivel; 401 | unsigned int group, offset, end, loop_start, loop_end; 402 | int transpose, tune, pitch_keycenter, pitch_keytrack; 403 | float attenuation, pan; 404 | struct tsf_envelope ampenv, modenv; 405 | int initialFilterQ, initialFilterFc; 406 | int modEnvToPitch, modEnvToFilterFc, modLfoToFilterFc, modLfoToVolume; 407 | float delayModLFO; 408 | int freqModLFO, modLfoToPitch; 409 | float delayVibLFO; 410 | int freqVibLFO, vibLfoToPitch; 411 | }; 412 | 413 | struct tsf_preset 414 | { 415 | tsf_char20 presetName; 416 | tsf_u16 preset, bank; 417 | struct tsf_region* regions; 418 | int regionNum; 419 | }; 420 | 421 | struct tsf_voice 422 | { 423 | int playingPreset, playingKey, playingChannel; 424 | struct tsf_region* region; 425 | double pitchInputTimecents, pitchOutputFactor; 426 | double sourceSamplePosition; 427 | float noteGainDB, panFactorLeft, panFactorRight; 428 | unsigned int playIndex, loopStart, loopEnd; 429 | struct tsf_voice_envelope ampenv, modenv; 430 | struct tsf_voice_lowpass lowpass; 431 | struct tsf_voice_lfo modlfo, viblfo; 432 | }; 433 | 434 | struct tsf_channel 435 | { 436 | unsigned short presetIndex, bank, pitchWheel, midiPan, midiVolume, midiExpression, midiRPN, midiData; 437 | float panOffset, gainDB, pitchRange, tuning; 438 | }; 439 | 440 | struct tsf_channels 441 | { 442 | void (*setupVoice)(tsf* f, struct tsf_voice* voice); 443 | struct tsf_channel* channels; 444 | int channelNum, activeChannel; 445 | }; 446 | 447 | static double tsf_timecents2Secsd(double timecents) { return TSF_POW(2.0, timecents / 1200.0); } 448 | static float tsf_timecents2Secsf(float timecents) { return TSF_POWF(2.0f, timecents / 1200.0f); } 449 | static float tsf_cents2Hertz(float cents) { return 8.176f * TSF_POWF(2.0f, cents / 1200.0f); } 450 | static float tsf_decibelsToGain(float db) { return (db > -100.f ? TSF_POWF(10.0f, db * 0.05f) : 0); } 451 | static float tsf_gainToDecibels(float gain) { return (gain <= .00001f ? -100.f : (float)(20.0 * TSF_LOG10(gain))); } 452 | 453 | static TSF_BOOL tsf_riffchunk_read(struct tsf_riffchunk* parent, struct tsf_riffchunk* chunk, struct tsf_stream* stream) 454 | { 455 | TSF_BOOL IsRiff, IsList; 456 | if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) > parent->size) return TSF_FALSE; 457 | if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; 458 | if (!stream->read(stream->data, &chunk->size, sizeof(tsf_u32))) return TSF_FALSE; 459 | if (parent && sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size > parent->size) return TSF_FALSE; 460 | if (parent) parent->size -= sizeof(tsf_fourcc) + sizeof(tsf_u32) + chunk->size; 461 | IsRiff = TSF_FourCCEquals(chunk->id, "RIFF"), IsList = TSF_FourCCEquals(chunk->id, "LIST"); 462 | if (IsRiff && parent) return TSF_FALSE; //not allowed 463 | if (!IsRiff && !IsList) return TSF_TRUE; //custom type without sub type 464 | if (!stream->read(stream->data, &chunk->id, sizeof(tsf_fourcc)) || *chunk->id <= ' ' || *chunk->id >= 'z') return TSF_FALSE; 465 | chunk->size -= sizeof(tsf_fourcc); 466 | return TSF_TRUE; 467 | } 468 | 469 | static void tsf_region_clear(struct tsf_region* i, TSF_BOOL for_relative) 470 | { 471 | TSF_MEMSET(i, 0, sizeof(struct tsf_region)); 472 | i->hikey = i->hivel = 127; 473 | i->pitch_keycenter = 60; // C4 474 | if (for_relative) return; 475 | 476 | i->pitch_keytrack = 100; 477 | 478 | i->pitch_keycenter = -1; 479 | 480 | // SF2 defaults in timecents. 481 | i->ampenv.delay = i->ampenv.attack = i->ampenv.hold = i->ampenv.decay = i->ampenv.release = -12000.0f; 482 | i->modenv.delay = i->modenv.attack = i->modenv.hold = i->modenv.decay = i->modenv.release = -12000.0f; 483 | 484 | i->initialFilterFc = 13500; 485 | 486 | i->delayModLFO = -12000.0f; 487 | i->delayVibLFO = -12000.0f; 488 | } 489 | 490 | static void tsf_region_operator(struct tsf_region* region, tsf_u16 genOper, union tsf_hydra_genamount* amount, struct tsf_region* merge_region) 491 | { 492 | enum 493 | { 494 | _GEN_TYPE_MASK = 0x0F, 495 | GEN_FLOAT = 0x01, 496 | GEN_INT = 0x02, 497 | GEN_UINT_ADD = 0x03, 498 | GEN_UINT_ADD15 = 0x04, 499 | GEN_KEYRANGE = 0x05, 500 | GEN_VELRANGE = 0x06, 501 | GEN_LOOPMODE = 0x07, 502 | GEN_GROUP = 0x08, 503 | GEN_KEYCENTER = 0x09, 504 | 505 | _GEN_LIMIT_MASK = 0xF0, 506 | GEN_INT_LIMIT12K = 0x10, //min -12000, max 12000 507 | GEN_INT_LIMITFC = 0x20, //min 1500, max 13500 508 | GEN_INT_LIMITQ = 0x30, //min 0, max 960 509 | GEN_INT_LIMIT960 = 0x40, //min -960, max 960 510 | GEN_INT_LIMIT16K4500 = 0x50, //min -16000, max 4500 511 | GEN_FLOAT_LIMIT12K5K = 0x60, //min -12000, max 5000 512 | GEN_FLOAT_LIMIT12K8K = 0x70, //min -12000, max 8000 513 | GEN_FLOAT_LIMIT1200 = 0x80, //min -1200, max 1200 514 | GEN_FLOAT_LIMITPAN = 0x90, //* .001f, min -.5f, max .5f, 515 | GEN_FLOAT_LIMITATTN = 0xA0, //* .1f, min 0, max 144.0 516 | GEN_FLOAT_MAX1000 = 0xB0, //min 0, max 1000 517 | GEN_FLOAT_MAX1440 = 0xC0, //min 0, max 1440 518 | 519 | _GEN_MAX = 59, 520 | }; 521 | #define _TSFREGIONOFFSET(TYPE, FIELD) (unsigned char)(((TYPE*)&((struct tsf_region*)0)->FIELD) - (TYPE*)0) 522 | #define _TSFREGIONENVOFFSET(TYPE, ENV, FIELD) (unsigned char)(((TYPE*)&((&(((struct tsf_region*)0)->ENV))->FIELD)) - (TYPE*)0) 523 | static const struct { unsigned char mode, offset; } genMetas[_GEN_MAX] = 524 | { 525 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, offset ) }, // 0 StartAddrsOffset 526 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, end ) }, // 1 EndAddrsOffset 527 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, loop_start ) }, // 2 StartloopAddrsOffset 528 | { GEN_UINT_ADD , _TSFREGIONOFFSET(unsigned int, loop_end ) }, // 3 EndloopAddrsOffset 529 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, offset ) }, // 4 StartAddrsCoarseOffset 530 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modLfoToPitch ) }, // 5 ModLfoToPitch 531 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, vibLfoToPitch ) }, // 6 VibLfoToPitch 532 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modEnvToPitch ) }, // 7 ModEnvToPitch 533 | { GEN_INT | GEN_INT_LIMITFC , _TSFREGIONOFFSET( int, initialFilterFc ) }, // 8 InitialFilterFc 534 | { GEN_INT | GEN_INT_LIMITQ , _TSFREGIONOFFSET( int, initialFilterQ ) }, // 9 InitialFilterQ 535 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modLfoToFilterFc ) }, //10 ModLfoToFilterFc 536 | { GEN_INT | GEN_INT_LIMIT12K , _TSFREGIONOFFSET( int, modEnvToFilterFc ) }, //11 ModEnvToFilterFc 537 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, end ) }, //12 EndAddrsCoarseOffset 538 | { GEN_INT | GEN_INT_LIMIT960 , _TSFREGIONOFFSET( int, modLfoToVolume ) }, //13 ModLfoToVolume 539 | { 0 , (0 ) }, // Unused 540 | { 0 , (0 ) }, //15 ChorusEffectsSend (unsupported) 541 | { 0 , (0 ) }, //16 ReverbEffectsSend (unsupported) 542 | { GEN_FLOAT | GEN_FLOAT_LIMITPAN , _TSFREGIONOFFSET( float, pan ) }, //17 Pan 543 | { 0 , (0 ) }, // Unused 544 | { 0 , (0 ) }, // Unused 545 | { 0 , (0 ) }, // Unused 546 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET( float, delayModLFO ) }, //21 DelayModLFO 547 | { GEN_INT | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET( int, freqModLFO ) }, //22 FreqModLFO 548 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONOFFSET( float, delayVibLFO ) }, //23 DelayVibLFO 549 | { GEN_INT | GEN_INT_LIMIT16K4500 , _TSFREGIONOFFSET( int, freqVibLFO ) }, //24 FreqVibLFO 550 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, modenv, delay ) }, //25 DelayModEnv 551 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, attack ) }, //26 AttackModEnv 552 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, modenv, hold ) }, //27 HoldModEnv 553 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, decay ) }, //28 DecayModEnv 554 | { GEN_FLOAT | GEN_FLOAT_MAX1000 , _TSFREGIONENVOFFSET( float, modenv, sustain ) }, //29 SustainModEnv 555 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, modenv, release ) }, //30 ReleaseModEnv 556 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, modenv, keynumToHold ) }, //31 KeynumToModEnvHold 557 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, modenv, keynumToDecay) }, //32 KeynumToModEnvDecay 558 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, ampenv, delay ) }, //33 DelayVolEnv 559 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, attack ) }, //34 AttackVolEnv 560 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K5K , _TSFREGIONENVOFFSET( float, ampenv, hold ) }, //35 HoldVolEnv 561 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, decay ) }, //36 DecayVolEnv 562 | { GEN_FLOAT | GEN_FLOAT_MAX1440 , _TSFREGIONENVOFFSET( float, ampenv, sustain ) }, //37 SustainVolEnv 563 | { GEN_FLOAT | GEN_FLOAT_LIMIT12K8K , _TSFREGIONENVOFFSET( float, ampenv, release ) }, //38 ReleaseVolEnv 564 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, ampenv, keynumToHold ) }, //39 KeynumToVolEnvHold 565 | { GEN_FLOAT | GEN_FLOAT_LIMIT1200 , _TSFREGIONENVOFFSET( float, ampenv, keynumToDecay) }, //40 KeynumToVolEnvDecay 566 | { 0 , (0 ) }, // Instrument (special) 567 | { 0 , (0 ) }, // Reserved 568 | { GEN_KEYRANGE , (0 ) }, //43 KeyRange 569 | { GEN_VELRANGE , (0 ) }, //44 VelRange 570 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, loop_start ) }, //45 StartloopAddrsCoarseOffset 571 | { 0 , (0 ) }, //46 Keynum (special) 572 | { 0 , (0 ) }, //47 Velocity (special) 573 | { GEN_FLOAT | GEN_FLOAT_LIMITATTN , _TSFREGIONOFFSET( float, attenuation ) }, //48 InitialAttenuation 574 | { 0 , (0 ) }, // Reserved 575 | { GEN_UINT_ADD15 , _TSFREGIONOFFSET(unsigned int, loop_end ) }, //50 EndloopAddrsCoarseOffset 576 | { GEN_INT , _TSFREGIONOFFSET( int, transpose ) }, //51 CoarseTune 577 | { GEN_INT , _TSFREGIONOFFSET( int, tune ) }, //52 FineTune 578 | { 0 , (0 ) }, // SampleID (special) 579 | { GEN_LOOPMODE , _TSFREGIONOFFSET( int, loop_mode ) }, //54 SampleModes 580 | { 0 , (0 ) }, // Reserved 581 | { GEN_INT , _TSFREGIONOFFSET( int, pitch_keytrack ) }, //56 ScaleTuning 582 | { GEN_GROUP , _TSFREGIONOFFSET(unsigned int, group ) }, //57 ExclusiveClass 583 | { GEN_KEYCENTER , _TSFREGIONOFFSET( int, pitch_keycenter ) }, //58 OverridingRootKey 584 | }; 585 | #undef _TSFREGIONOFFSET 586 | #undef _TSFREGIONENVOFFSET 587 | if (amount) 588 | { 589 | int offset; 590 | if (genOper >= _GEN_MAX) return; 591 | offset = genMetas[genOper].offset; 592 | switch (genMetas[genOper].mode & _GEN_TYPE_MASK) 593 | { 594 | case GEN_FLOAT: (( float*)region)[offset] = amount->shortAmount; return; 595 | case GEN_INT: (( int*)region)[offset] = amount->shortAmount; return; 596 | case GEN_UINT_ADD: ((unsigned int*)region)[offset] += amount->shortAmount; return; 597 | case GEN_UINT_ADD15: ((unsigned int*)region)[offset] += amount->shortAmount<<15; return; 598 | case GEN_KEYRANGE: region->lokey = amount->range.lo; region->hikey = amount->range.hi; return; 599 | case GEN_VELRANGE: region->lovel = amount->range.lo; region->hivel = amount->range.hi; return; 600 | case GEN_LOOPMODE: region->loop_mode = ((amount->wordAmount&3) == 3 ? TSF_LOOPMODE_SUSTAIN : ((amount->wordAmount&3) == 1 ? TSF_LOOPMODE_CONTINUOUS : TSF_LOOPMODE_NONE)); return; 601 | case GEN_GROUP: region->group = amount->wordAmount; return; 602 | case GEN_KEYCENTER: region->pitch_keycenter = amount->shortAmount; return; 603 | } 604 | } 605 | else //merge regions and clamp values 606 | { 607 | for (genOper = 0; genOper != _GEN_MAX; genOper++) 608 | { 609 | int offset = genMetas[genOper].offset; 610 | switch (genMetas[genOper].mode & _GEN_TYPE_MASK) 611 | { 612 | case GEN_FLOAT: 613 | { 614 | float *val = &((float*)region)[offset], vfactor, vmin, vmax; 615 | *val += ((float*)merge_region)[offset]; 616 | switch (genMetas[genOper].mode & _GEN_LIMIT_MASK) 617 | { 618 | case GEN_FLOAT_LIMIT12K5K: vfactor = 1.0f; vmin = -12000.0f; vmax = 5000.0f; break; 619 | case GEN_FLOAT_LIMIT12K8K: vfactor = 1.0f; vmin = -12000.0f; vmax = 8000.0f; break; 620 | case GEN_FLOAT_LIMIT1200: vfactor = 1.0f; vmin = -1200.0f; vmax = 1200.0f; break; 621 | case GEN_FLOAT_LIMITPAN: vfactor = 0.001f; vmin = -0.5f; vmax = 0.5f; break; 622 | case GEN_FLOAT_LIMITATTN: vfactor = 0.1f; vmin = 0.0f; vmax = 144.0f; break; 623 | case GEN_FLOAT_MAX1000: vfactor = 1.0f; vmin = 0.0f; vmax = 1000.0f; break; 624 | case GEN_FLOAT_MAX1440: vfactor = 1.0f; vmin = 0.0f; vmax = 1440.0f; break; 625 | default: continue; 626 | } 627 | *val *= vfactor; 628 | if (*val < vmin) *val = vmin; 629 | else if (*val > vmax) *val = vmax; 630 | continue; 631 | } 632 | case GEN_INT: 633 | { 634 | int *val = &((int*)region)[offset], vmin, vmax; 635 | *val += ((int*)merge_region)[offset]; 636 | switch (genMetas[genOper].mode & _GEN_LIMIT_MASK) 637 | { 638 | case GEN_INT_LIMIT12K: vmin = -12000; vmax = 12000; break; 639 | case GEN_INT_LIMITFC: vmin = 1500; vmax = 13500; break; 640 | case GEN_INT_LIMITQ: vmin = 0; vmax = 960; break; 641 | case GEN_INT_LIMIT960: vmin = -960; vmax = 960; break; 642 | case GEN_INT_LIMIT16K4500: vmin = -16000; vmax = 4500; break; 643 | default: continue; 644 | } 645 | if (*val < vmin) *val = vmin; 646 | else if (*val > vmax) *val = vmax; 647 | continue; 648 | } 649 | case GEN_UINT_ADD: 650 | { 651 | ((unsigned int*)region)[offset] += ((unsigned int*)merge_region)[offset]; 652 | continue; 653 | } 654 | } 655 | } 656 | } 657 | } 658 | 659 | static void tsf_region_envtosecs(struct tsf_envelope* p, TSF_BOOL sustainIsGain) 660 | { 661 | // EG times need to be converted from timecents to seconds. 662 | // Pin very short EG segments. Timecents don't get to zero, and our EG is 663 | // happier with zero values. 664 | p->delay = (p->delay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->delay)); 665 | p->attack = (p->attack < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->attack)); 666 | p->release = (p->release < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->release)); 667 | 668 | // If we have dynamic hold or decay times depending on key number we need 669 | // to keep the values in timecents so we can calculate it during startNote 670 | if (!p->keynumToHold) p->hold = (p->hold < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->hold)); 671 | if (!p->keynumToDecay) p->decay = (p->decay < -11950.0f ? 0.0f : tsf_timecents2Secsf(p->decay)); 672 | 673 | if (p->sustain < 0.0f) p->sustain = 0.0f; 674 | else if (sustainIsGain) p->sustain = tsf_decibelsToGain(-p->sustain / 10.0f); 675 | else p->sustain = 1.0f - (p->sustain / 1000.0f); 676 | } 677 | 678 | static void tsf_load_presets(tsf* res, struct tsf_hydra *hydra, unsigned int fontSampleCount) 679 | { 680 | enum { GenInstrument = 41, GenKeyRange = 43, GenVelRange = 44, GenSampleID = 53 }; 681 | // Read each preset. 682 | struct tsf_hydra_phdr *pphdr, *pphdrMax; 683 | for (pphdr = hydra->phdrs, pphdrMax = pphdr + hydra->phdrNum - 1; pphdr != pphdrMax; pphdr++) 684 | { 685 | int sortedIndex = 0, region_index = 0; 686 | struct tsf_hydra_phdr *otherphdr; 687 | struct tsf_preset* preset; 688 | struct tsf_hydra_pbag *ppbag, *ppbagEnd; 689 | struct tsf_region globalRegion; 690 | for (otherphdr = hydra->phdrs; otherphdr != pphdrMax; otherphdr++) 691 | { 692 | if (otherphdr == pphdr || otherphdr->bank > pphdr->bank) continue; 693 | else if (otherphdr->bank < pphdr->bank) sortedIndex++; 694 | else if (otherphdr->preset > pphdr->preset) continue; 695 | else if (otherphdr->preset < pphdr->preset) sortedIndex++; 696 | else if (otherphdr < pphdr) sortedIndex++; 697 | } 698 | 699 | preset = &res->presets[sortedIndex]; 700 | TSF_MEMCPY(preset->presetName, pphdr->presetName, sizeof(preset->presetName)); 701 | preset->presetName[sizeof(preset->presetName)-1] = '\0'; //should be zero terminated in source file but make sure 702 | preset->bank = pphdr->bank; 703 | preset->preset = pphdr->preset; 704 | preset->regionNum = 0; 705 | 706 | //count regions covered by this preset 707 | for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) 708 | { 709 | unsigned char plokey = 0, phikey = 127, plovel = 0, phivel = 127; 710 | struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; 711 | for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) 712 | { 713 | if (ppgen->genOper == GenKeyRange) { plokey = ppgen->genAmount.range.lo; phikey = ppgen->genAmount.range.hi; continue; } 714 | if (ppgen->genOper == GenVelRange) { plovel = ppgen->genAmount.range.lo; phivel = ppgen->genAmount.range.hi; continue; } 715 | if (ppgen->genOper != GenInstrument) continue; 716 | if (ppgen->genAmount.wordAmount >= hydra->instNum) continue; 717 | pinst = hydra->insts + ppgen->genAmount.wordAmount; 718 | for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) 719 | { 720 | unsigned char ilokey = 0, ihikey = 127, ilovel = 0, ihivel = 127; 721 | for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) 722 | { 723 | if (pigen->genOper == GenKeyRange) { ilokey = pigen->genAmount.range.lo; ihikey = pigen->genAmount.range.hi; continue; } 724 | if (pigen->genOper == GenVelRange) { ilovel = pigen->genAmount.range.lo; ihivel = pigen->genAmount.range.hi; continue; } 725 | if (pigen->genOper == GenSampleID && ihikey >= plokey && ilokey <= phikey && ihivel >= plovel && ilovel <= phivel) preset->regionNum++; 726 | } 727 | } 728 | } 729 | } 730 | 731 | preset->regions = (struct tsf_region*)TSF_MALLOC(preset->regionNum * sizeof(struct tsf_region)); 732 | tsf_region_clear(&globalRegion, TSF_TRUE); 733 | 734 | // Zones. 735 | for (ppbag = hydra->pbags + pphdr->presetBagNdx, ppbagEnd = hydra->pbags + pphdr[1].presetBagNdx; ppbag != ppbagEnd; ppbag++) 736 | { 737 | struct tsf_hydra_pgen *ppgen, *ppgenEnd; struct tsf_hydra_inst *pinst; struct tsf_hydra_ibag *pibag, *pibagEnd; struct tsf_hydra_igen *pigen, *pigenEnd; 738 | struct tsf_region presetRegion = globalRegion; 739 | int hadGenInstrument = 0; 740 | 741 | // Generators. 742 | for (ppgen = hydra->pgens + ppbag->genNdx, ppgenEnd = hydra->pgens + ppbag[1].genNdx; ppgen != ppgenEnd; ppgen++) 743 | { 744 | // Instrument. 745 | if (ppgen->genOper == GenInstrument) 746 | { 747 | struct tsf_region instRegion; 748 | tsf_u16 whichInst = ppgen->genAmount.wordAmount; 749 | if (whichInst >= hydra->instNum) continue; 750 | 751 | tsf_region_clear(&instRegion, TSF_FALSE); 752 | pinst = &hydra->insts[whichInst]; 753 | for (pibag = hydra->ibags + pinst->instBagNdx, pibagEnd = hydra->ibags + pinst[1].instBagNdx; pibag != pibagEnd; pibag++) 754 | { 755 | // Generators. 756 | struct tsf_region zoneRegion = instRegion; 757 | int hadSampleID = 0; 758 | for (pigen = hydra->igens + pibag->instGenNdx, pigenEnd = hydra->igens + pibag[1].instGenNdx; pigen != pigenEnd; pigen++) 759 | { 760 | if (pigen->genOper == GenSampleID) 761 | { 762 | struct tsf_hydra_shdr* pshdr; 763 | 764 | //preset region key and vel ranges are a filter for the zone regions 765 | if (zoneRegion.hikey < presetRegion.lokey || zoneRegion.lokey > presetRegion.hikey) continue; 766 | if (zoneRegion.hivel < presetRegion.lovel || zoneRegion.lovel > presetRegion.hivel) continue; 767 | if (presetRegion.lokey > zoneRegion.lokey) zoneRegion.lokey = presetRegion.lokey; 768 | if (presetRegion.hikey < zoneRegion.hikey) zoneRegion.hikey = presetRegion.hikey; 769 | if (presetRegion.lovel > zoneRegion.lovel) zoneRegion.lovel = presetRegion.lovel; 770 | if (presetRegion.hivel < zoneRegion.hivel) zoneRegion.hivel = presetRegion.hivel; 771 | 772 | //sum regions 773 | tsf_region_operator(&zoneRegion, 0, TSF_NULL, &presetRegion); 774 | 775 | // EG times need to be converted from timecents to seconds. 776 | tsf_region_envtosecs(&zoneRegion.ampenv, TSF_TRUE); 777 | tsf_region_envtosecs(&zoneRegion.modenv, TSF_FALSE); 778 | 779 | // LFO times need to be converted from timecents to seconds. 780 | zoneRegion.delayModLFO = (zoneRegion.delayModLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayModLFO)); 781 | zoneRegion.delayVibLFO = (zoneRegion.delayVibLFO < -11950.0f ? 0.0f : tsf_timecents2Secsf(zoneRegion.delayVibLFO)); 782 | 783 | // Fixup sample positions 784 | pshdr = &hydra->shdrs[pigen->genAmount.wordAmount]; 785 | zoneRegion.offset += pshdr->start; 786 | zoneRegion.end += pshdr->end; 787 | zoneRegion.loop_start += pshdr->startLoop; 788 | zoneRegion.loop_end += pshdr->endLoop; 789 | if (pshdr->endLoop > 0) zoneRegion.loop_end -= 1; 790 | if (zoneRegion.pitch_keycenter == -1) zoneRegion.pitch_keycenter = pshdr->originalPitch; 791 | zoneRegion.tune += pshdr->pitchCorrection; 792 | zoneRegion.sample_rate = pshdr->sampleRate; 793 | if (zoneRegion.end && zoneRegion.end < fontSampleCount) zoneRegion.end++; 794 | else zoneRegion.end = fontSampleCount; 795 | 796 | preset->regions[region_index] = zoneRegion; 797 | region_index++; 798 | hadSampleID = 1; 799 | } 800 | else tsf_region_operator(&zoneRegion, pigen->genOper, &pigen->genAmount, TSF_NULL); 801 | } 802 | 803 | // Handle instrument's global zone. 804 | if (pibag == hydra->ibags + pinst->instBagNdx && !hadSampleID) 805 | instRegion = zoneRegion; 806 | 807 | // Modulators (TODO) 808 | //if (ibag->instModNdx < ibag[1].instModNdx) addUnsupportedOpcode("any modulator"); 809 | } 810 | hadGenInstrument = 1; 811 | } 812 | else tsf_region_operator(&presetRegion, ppgen->genOper, &ppgen->genAmount, TSF_NULL); 813 | } 814 | 815 | // Modulators (TODO) 816 | //if (pbag->modNdx < pbag[1].modNdx) addUnsupportedOpcode("any modulator"); 817 | 818 | // Handle preset's global zone. 819 | if (ppbag == hydra->pbags + pphdr->presetBagNdx && !hadGenInstrument) 820 | globalRegion = presetRegion; 821 | } 822 | } 823 | } 824 | 825 | static void tsf_load_samples(float** fontSamples, unsigned int* fontSampleCount, struct tsf_riffchunk *chunkSmpl, struct tsf_stream* stream) 826 | { 827 | // Read sample data into float format buffer. 828 | float* out; unsigned int samplesLeft, samplesToRead, samplesToConvert; 829 | samplesLeft = *fontSampleCount = chunkSmpl->size / sizeof(short); 830 | out = *fontSamples = (float*)TSF_MALLOC(samplesLeft * sizeof(float)); 831 | for (; samplesLeft; samplesLeft -= samplesToRead) 832 | { 833 | short sampleBuffer[1024], *in = sampleBuffer;; 834 | samplesToRead = (samplesLeft > 1024 ? 1024 : samplesLeft); 835 | stream->read(stream->data, sampleBuffer, samplesToRead * sizeof(short)); 836 | 837 | // Convert from signed 16-bit to float. 838 | for (samplesToConvert = samplesToRead; samplesToConvert > 0; --samplesToConvert) 839 | // If we ever need to compile for big-endian platforms, we'll need to byte-swap here. 840 | *out++ = (float)(*in++ / 32767.0); 841 | } 842 | } 843 | 844 | static void tsf_voice_envelope_nextsegment(struct tsf_voice_envelope* e, short active_segment, float outSampleRate) 845 | { 846 | switch (active_segment) 847 | { 848 | case TSF_SEGMENT_NONE: 849 | e->samplesUntilNextSegment = (int)(e->parameters.delay * outSampleRate); 850 | if (e->samplesUntilNextSegment > 0) 851 | { 852 | e->segment = TSF_SEGMENT_DELAY; 853 | e->segmentIsExponential = TSF_FALSE; 854 | e->level = 0.0; 855 | e->slope = 0.0; 856 | return; 857 | } 858 | /* fall through */ 859 | case TSF_SEGMENT_DELAY: 860 | e->samplesUntilNextSegment = (int)(e->parameters.attack * outSampleRate); 861 | if (e->samplesUntilNextSegment > 0) 862 | { 863 | if (!e->isAmpEnv) 864 | { 865 | //mod env attack duration scales with velocity (velocity of 1 is full duration, max velocity is 0.125 times duration) 866 | e->samplesUntilNextSegment = (int)(e->parameters.attack * ((145 - e->midiVelocity) / 144.0f) * outSampleRate); 867 | } 868 | e->segment = TSF_SEGMENT_ATTACK; 869 | e->segmentIsExponential = TSF_FALSE; 870 | e->level = 0.0f; 871 | e->slope = 1.0f / e->samplesUntilNextSegment; 872 | return; 873 | } 874 | /* fall through */ 875 | case TSF_SEGMENT_ATTACK: 876 | e->samplesUntilNextSegment = (int)(e->parameters.hold * outSampleRate); 877 | if (e->samplesUntilNextSegment > 0) 878 | { 879 | e->segment = TSF_SEGMENT_HOLD; 880 | e->segmentIsExponential = TSF_FALSE; 881 | e->level = 1.0f; 882 | e->slope = 0.0f; 883 | return; 884 | } 885 | /* fall through */ 886 | case TSF_SEGMENT_HOLD: 887 | e->samplesUntilNextSegment = (int)(e->parameters.decay * outSampleRate); 888 | if (e->samplesUntilNextSegment > 0) 889 | { 890 | e->segment = TSF_SEGMENT_DECAY; 891 | e->level = 1.0f; 892 | if (e->isAmpEnv) 893 | { 894 | // I don't truly understand this; just following what LinuxSampler does. 895 | float mysterySlope = -9.226f / e->samplesUntilNextSegment; 896 | e->slope = TSF_EXPF(mysterySlope); 897 | e->segmentIsExponential = TSF_TRUE; 898 | if (e->parameters.sustain > 0.0f) 899 | { 900 | // Again, this is following LinuxSampler's example, which is similar to 901 | // SF2-style decay, where "decay" specifies the time it would take to 902 | // get to zero, not to the sustain level. The SFZ spec is not that 903 | // specific about what "decay" means, so perhaps it's really supposed 904 | // to specify the time to reach the sustain level. 905 | e->samplesUntilNextSegment = (int)(TSF_LOG(e->parameters.sustain) / mysterySlope); 906 | } 907 | } 908 | else 909 | { 910 | e->slope = -1.0f / e->samplesUntilNextSegment; 911 | e->samplesUntilNextSegment = (int)(e->parameters.decay * (1.0f - e->parameters.sustain) * outSampleRate); 912 | e->segmentIsExponential = TSF_FALSE; 913 | } 914 | return; 915 | } 916 | /* fall through */ 917 | case TSF_SEGMENT_DECAY: 918 | e->segment = TSF_SEGMENT_SUSTAIN; 919 | e->level = e->parameters.sustain; 920 | e->slope = 0.0f; 921 | e->samplesUntilNextSegment = 0x7FFFFFFF; 922 | e->segmentIsExponential = TSF_FALSE; 923 | return; 924 | case TSF_SEGMENT_SUSTAIN: 925 | e->segment = TSF_SEGMENT_RELEASE; 926 | e->samplesUntilNextSegment = (int)((e->parameters.release <= 0 ? TSF_FASTRELEASETIME : e->parameters.release) * outSampleRate); 927 | if (e->isAmpEnv) 928 | { 929 | // I don't truly understand this; just following what LinuxSampler does. 930 | float mysterySlope = -9.226f / e->samplesUntilNextSegment; 931 | e->slope = TSF_EXPF(mysterySlope); 932 | e->segmentIsExponential = TSF_TRUE; 933 | } 934 | else 935 | { 936 | e->slope = -e->level / e->samplesUntilNextSegment; 937 | e->segmentIsExponential = TSF_FALSE; 938 | } 939 | return; 940 | case TSF_SEGMENT_RELEASE: 941 | default: 942 | e->segment = TSF_SEGMENT_DONE; 943 | e->segmentIsExponential = TSF_FALSE; 944 | e->level = e->slope = 0.0f; 945 | e->samplesUntilNextSegment = 0x7FFFFFF; 946 | } 947 | } 948 | 949 | static void tsf_voice_envelope_setup(struct tsf_voice_envelope* e, struct tsf_envelope* new_parameters, int midiNoteNumber, short midiVelocity, TSF_BOOL isAmpEnv, float outSampleRate) 950 | { 951 | e->parameters = *new_parameters; 952 | if (e->parameters.keynumToHold) 953 | { 954 | e->parameters.hold += e->parameters.keynumToHold * (60.0f - midiNoteNumber); 955 | e->parameters.hold = (e->parameters.hold < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.hold)); 956 | } 957 | if (e->parameters.keynumToDecay) 958 | { 959 | e->parameters.decay += e->parameters.keynumToDecay * (60.0f - midiNoteNumber); 960 | e->parameters.decay = (e->parameters.decay < -10000.0f ? 0.0f : tsf_timecents2Secsf(e->parameters.decay)); 961 | } 962 | e->midiVelocity = midiVelocity; 963 | e->isAmpEnv = isAmpEnv; 964 | tsf_voice_envelope_nextsegment(e, TSF_SEGMENT_NONE, outSampleRate); 965 | } 966 | 967 | static void tsf_voice_envelope_process(struct tsf_voice_envelope* e, int numSamples, float outSampleRate) 968 | { 969 | if (e->slope) 970 | { 971 | if (e->segmentIsExponential) e->level *= TSF_POWF(e->slope, (float)numSamples); 972 | else e->level += (e->slope * numSamples); 973 | } 974 | if ((e->samplesUntilNextSegment -= numSamples) <= 0) 975 | tsf_voice_envelope_nextsegment(e, e->segment, outSampleRate); 976 | } 977 | 978 | static void tsf_voice_lowpass_setup(struct tsf_voice_lowpass* e, float Fc) 979 | { 980 | // Lowpass filter from http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/ 981 | double K = TSF_TAN(TSF_PI * Fc), KK = K * K; 982 | double norm = 1 / (1 + K * e->QInv + KK); 983 | e->a0 = KK * norm; 984 | e->a1 = 2 * e->a0; 985 | e->b1 = 2 * (KK - 1) * norm; 986 | e->b2 = (1 - K * e->QInv + KK) * norm; 987 | } 988 | 989 | static float tsf_voice_lowpass_process(struct tsf_voice_lowpass* e, double In) 990 | { 991 | double Out = In * e->a0 + e->z1; e->z1 = In * e->a1 + e->z2 - e->b1 * Out; e->z2 = In * e->a0 - e->b2 * Out; return (float)Out; 992 | } 993 | 994 | static void tsf_voice_lfo_setup(struct tsf_voice_lfo* e, float delay, int freqCents, float outSampleRate) 995 | { 996 | e->samplesUntil = (int)(delay * outSampleRate); 997 | e->delta = (4.0f * tsf_cents2Hertz((float)freqCents) / outSampleRate); 998 | e->level = 0; 999 | } 1000 | 1001 | static void tsf_voice_lfo_process(struct tsf_voice_lfo* e, int blockSamples) 1002 | { 1003 | if (e->samplesUntil > blockSamples) { e->samplesUntil -= blockSamples; return; } 1004 | e->level += e->delta * blockSamples; 1005 | if (e->level > 1.0f) { e->delta = -e->delta; e->level = 2.0f - e->level; } 1006 | else if (e->level < -1.0f) { e->delta = -e->delta; e->level = -2.0f - e->level; } 1007 | } 1008 | 1009 | static void tsf_voice_kill(struct tsf_voice* v) 1010 | { 1011 | v->playingPreset = -1; 1012 | } 1013 | 1014 | static void tsf_voice_end(tsf* f, struct tsf_voice* v) 1015 | { 1016 | // if maxVoiceNum is set, assume that voice rendering and note queuing are on sparate threads 1017 | // so to minimize the chance that voice rendering would advance the segment at the same time 1018 | // we just do it twice here and hope that it sticks 1019 | int repeats = (f->maxVoiceNum ? 2 : 1); 1020 | while (repeats--) 1021 | { 1022 | tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1023 | tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1024 | if (v->region->loop_mode == TSF_LOOPMODE_SUSTAIN) 1025 | { 1026 | // Continue playing, but stop looping. 1027 | v->loopEnd = v->loopStart; 1028 | } 1029 | } 1030 | } 1031 | 1032 | static void tsf_voice_endquick(tsf* f, struct tsf_voice* v) 1033 | { 1034 | // if maxVoiceNum is set, assume that voice rendering and note queuing are on sparate threads 1035 | // so to minimize the chance that voice rendering would advance the segment at the same time 1036 | // we just do it twice here and hope that it sticks 1037 | int repeats = (f->maxVoiceNum ? 2 : 1); 1038 | while (repeats--) 1039 | { 1040 | v->ampenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->ampenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1041 | v->modenv.parameters.release = 0.0f; tsf_voice_envelope_nextsegment(&v->modenv, TSF_SEGMENT_SUSTAIN, f->outSampleRate); 1042 | } 1043 | } 1044 | 1045 | static void tsf_voice_calcpitchratio(struct tsf_voice* v, float pitchShift, float outSampleRate) 1046 | { 1047 | double note = v->playingKey + v->region->transpose + v->region->tune / 100.0; 1048 | double adjustedPitch = v->region->pitch_keycenter + (note - v->region->pitch_keycenter) * (v->region->pitch_keytrack / 100.0); 1049 | if (pitchShift) adjustedPitch += pitchShift; 1050 | v->pitchInputTimecents = adjustedPitch * 100.0; 1051 | v->pitchOutputFactor = v->region->sample_rate / (tsf_timecents2Secsd(v->region->pitch_keycenter * 100.0) * outSampleRate); 1052 | } 1053 | 1054 | static void tsf_voice_render(tsf* f, struct tsf_voice* v, float* outputBuffer, int numSamples) 1055 | { 1056 | struct tsf_region* region = v->region; 1057 | float* input = f->fontSamples; 1058 | float* outL = outputBuffer; 1059 | float* outR = (f->outputmode == TSF_STEREO_UNWEAVED ? outL + numSamples : TSF_NULL); 1060 | 1061 | // Cache some values, to give them at least some chance of ending up in registers. 1062 | TSF_BOOL updateModEnv = (region->modEnvToPitch || region->modEnvToFilterFc); 1063 | TSF_BOOL updateModLFO = (v->modlfo.delta && (region->modLfoToPitch || region->modLfoToFilterFc || region->modLfoToVolume)); 1064 | TSF_BOOL updateVibLFO = (v->viblfo.delta && (region->vibLfoToPitch)); 1065 | TSF_BOOL isLooping = (v->loopStart < v->loopEnd); 1066 | unsigned int tmpLoopStart = v->loopStart, tmpLoopEnd = v->loopEnd; 1067 | double tmpSampleEndDbl = (double)region->end, tmpLoopEndDbl = (double)tmpLoopEnd + 1.0; 1068 | double tmpSourceSamplePosition = v->sourceSamplePosition; 1069 | struct tsf_voice_lowpass tmpLowpass = v->lowpass; 1070 | 1071 | TSF_BOOL dynamicLowpass = (region->modLfoToFilterFc || region->modEnvToFilterFc); 1072 | float tmpSampleRate = f->outSampleRate, tmpInitialFilterFc, tmpModLfoToFilterFc, tmpModEnvToFilterFc; 1073 | 1074 | TSF_BOOL dynamicPitchRatio = (region->modLfoToPitch || region->modEnvToPitch || region->vibLfoToPitch); 1075 | double pitchRatio; 1076 | float tmpModLfoToPitch, tmpVibLfoToPitch, tmpModEnvToPitch; 1077 | 1078 | TSF_BOOL dynamicGain = (region->modLfoToVolume != 0); 1079 | float noteGain = 0, tmpModLfoToVolume; 1080 | 1081 | if (dynamicLowpass) tmpInitialFilterFc = (float)region->initialFilterFc, tmpModLfoToFilterFc = (float)region->modLfoToFilterFc, tmpModEnvToFilterFc = (float)region->modEnvToFilterFc; 1082 | else tmpInitialFilterFc = 0, tmpModLfoToFilterFc = 0, tmpModEnvToFilterFc = 0; 1083 | 1084 | if (dynamicPitchRatio) pitchRatio = 0, tmpModLfoToPitch = (float)region->modLfoToPitch, tmpVibLfoToPitch = (float)region->vibLfoToPitch, tmpModEnvToPitch = (float)region->modEnvToPitch; 1085 | else pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents) * v->pitchOutputFactor, tmpModLfoToPitch = 0, tmpVibLfoToPitch = 0, tmpModEnvToPitch = 0; 1086 | 1087 | if (dynamicGain) tmpModLfoToVolume = (float)region->modLfoToVolume * 0.1f; 1088 | else noteGain = tsf_decibelsToGain(v->noteGainDB), tmpModLfoToVolume = 0; 1089 | 1090 | while (numSamples) 1091 | { 1092 | float gainMono, gainLeft, gainRight; 1093 | int blockSamples = (numSamples > TSF_RENDER_EFFECTSAMPLEBLOCK ? TSF_RENDER_EFFECTSAMPLEBLOCK : numSamples); 1094 | numSamples -= blockSamples; 1095 | 1096 | if (dynamicLowpass) 1097 | { 1098 | float fres = tmpInitialFilterFc + v->modlfo.level * tmpModLfoToFilterFc + v->modenv.level * tmpModEnvToFilterFc; 1099 | float lowpassFc = (fres <= 13500 ? tsf_cents2Hertz(fres) / tmpSampleRate : 1.0f); 1100 | tmpLowpass.active = (lowpassFc < 0.499f); 1101 | if (tmpLowpass.active) tsf_voice_lowpass_setup(&tmpLowpass, lowpassFc); 1102 | } 1103 | 1104 | if (dynamicPitchRatio) 1105 | pitchRatio = tsf_timecents2Secsd(v->pitchInputTimecents + (v->modlfo.level * tmpModLfoToPitch + v->viblfo.level * tmpVibLfoToPitch + v->modenv.level * tmpModEnvToPitch)) * v->pitchOutputFactor; 1106 | 1107 | if (dynamicGain) 1108 | noteGain = tsf_decibelsToGain(v->noteGainDB + (v->modlfo.level * tmpModLfoToVolume)); 1109 | 1110 | gainMono = noteGain * v->ampenv.level; 1111 | 1112 | // Update EG. 1113 | tsf_voice_envelope_process(&v->ampenv, blockSamples, tmpSampleRate); 1114 | if (updateModEnv) tsf_voice_envelope_process(&v->modenv, blockSamples, tmpSampleRate); 1115 | 1116 | // Update LFOs. 1117 | if (updateModLFO) tsf_voice_lfo_process(&v->modlfo, blockSamples); 1118 | if (updateVibLFO) tsf_voice_lfo_process(&v->viblfo, blockSamples); 1119 | 1120 | switch (f->outputmode) 1121 | { 1122 | case TSF_STEREO_INTERLEAVED: 1123 | gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; 1124 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) 1125 | { 1126 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); 1127 | 1128 | // Simple linear interpolation. 1129 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); 1130 | 1131 | // Low-pass filter. 1132 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); 1133 | 1134 | *outL++ += val * gainLeft; 1135 | *outL++ += val * gainRight; 1136 | 1137 | // Next sample. 1138 | tmpSourceSamplePosition += pitchRatio; 1139 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); 1140 | } 1141 | break; 1142 | 1143 | case TSF_STEREO_UNWEAVED: 1144 | gainLeft = gainMono * v->panFactorLeft, gainRight = gainMono * v->panFactorRight; 1145 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) 1146 | { 1147 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); 1148 | 1149 | // Simple linear interpolation. 1150 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); 1151 | 1152 | // Low-pass filter. 1153 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); 1154 | 1155 | *outL++ += val * gainLeft; 1156 | *outR++ += val * gainRight; 1157 | 1158 | // Next sample. 1159 | tmpSourceSamplePosition += pitchRatio; 1160 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); 1161 | } 1162 | break; 1163 | 1164 | case TSF_MONO: 1165 | while (blockSamples-- && tmpSourceSamplePosition < tmpSampleEndDbl) 1166 | { 1167 | unsigned int pos = (unsigned int)tmpSourceSamplePosition, nextPos = (pos >= tmpLoopEnd && isLooping ? tmpLoopStart : pos + 1); 1168 | 1169 | // Simple linear interpolation. 1170 | float alpha = (float)(tmpSourceSamplePosition - pos), val = (input[pos] * (1.0f - alpha) + input[nextPos] * alpha); 1171 | 1172 | // Low-pass filter. 1173 | if (tmpLowpass.active) val = tsf_voice_lowpass_process(&tmpLowpass, val); 1174 | 1175 | *outL++ += val * gainMono; 1176 | 1177 | // Next sample. 1178 | tmpSourceSamplePosition += pitchRatio; 1179 | if (tmpSourceSamplePosition >= tmpLoopEndDbl && isLooping) tmpSourceSamplePosition -= (tmpLoopEnd - tmpLoopStart + 1.0); 1180 | } 1181 | break; 1182 | } 1183 | 1184 | if (tmpSourceSamplePosition >= tmpSampleEndDbl || v->ampenv.segment == TSF_SEGMENT_DONE) 1185 | { 1186 | tsf_voice_kill(v); 1187 | return; 1188 | } 1189 | } 1190 | 1191 | v->sourceSamplePosition = tmpSourceSamplePosition; 1192 | if (tmpLowpass.active || dynamicLowpass) v->lowpass = tmpLowpass; 1193 | } 1194 | 1195 | TSFDEF tsf* tsf_load(struct tsf_stream* stream) 1196 | { 1197 | tsf* res = TSF_NULL; 1198 | struct tsf_riffchunk chunkHead; 1199 | struct tsf_riffchunk chunkList; 1200 | struct tsf_hydra hydra; 1201 | float* fontSamples = TSF_NULL; 1202 | unsigned int fontSampleCount = 0; 1203 | 1204 | if (!tsf_riffchunk_read(TSF_NULL, &chunkHead, stream) || !TSF_FourCCEquals(chunkHead.id, "sfbk")) 1205 | { 1206 | //if (e) *e = TSF_INVALID_NOSF2HEADER; 1207 | return res; 1208 | } 1209 | 1210 | // Read hydra and locate sample data. 1211 | TSF_MEMSET(&hydra, 0, sizeof(hydra)); 1212 | while (tsf_riffchunk_read(&chunkHead, &chunkList, stream)) 1213 | { 1214 | struct tsf_riffchunk chunk; 1215 | if (TSF_FourCCEquals(chunkList.id, "pdta")) 1216 | { 1217 | while (tsf_riffchunk_read(&chunkList, &chunk, stream)) 1218 | { 1219 | #define HandleChunk(chunkName) (TSF_FourCCEquals(chunk.id, #chunkName) && !(chunk.size % chunkName##SizeInFile)) \ 1220 | { \ 1221 | int num = chunk.size / chunkName##SizeInFile, i; \ 1222 | hydra.chunkName##Num = num; \ 1223 | hydra.chunkName##s = (struct tsf_hydra_##chunkName*)TSF_MALLOC(num * sizeof(struct tsf_hydra_##chunkName)); \ 1224 | for (i = 0; i < num; ++i) tsf_hydra_read_##chunkName(&hydra.chunkName##s[i], stream); \ 1225 | } 1226 | enum 1227 | { 1228 | phdrSizeInFile = 38, pbagSizeInFile = 4, pmodSizeInFile = 10, 1229 | pgenSizeInFile = 4, instSizeInFile = 22, ibagSizeInFile = 4, 1230 | imodSizeInFile = 10, igenSizeInFile = 4, shdrSizeInFile = 46 1231 | }; 1232 | if HandleChunk(phdr) else if HandleChunk(pbag) else if HandleChunk(pmod) 1233 | else if HandleChunk(pgen) else if HandleChunk(inst) else if HandleChunk(ibag) 1234 | else if HandleChunk(imod) else if HandleChunk(igen) else if HandleChunk(shdr) 1235 | else stream->skip(stream->data, chunk.size); 1236 | #undef HandleChunk 1237 | } 1238 | } 1239 | else if (TSF_FourCCEquals(chunkList.id, "sdta")) 1240 | { 1241 | while (tsf_riffchunk_read(&chunkList, &chunk, stream)) 1242 | { 1243 | if (TSF_FourCCEquals(chunk.id, "smpl")) 1244 | { 1245 | tsf_load_samples(&fontSamples, &fontSampleCount, &chunk, stream); 1246 | } 1247 | else stream->skip(stream->data, chunk.size); 1248 | } 1249 | } 1250 | else stream->skip(stream->data, chunkList.size); 1251 | } 1252 | if (!hydra.phdrs || !hydra.pbags || !hydra.pmods || !hydra.pgens || !hydra.insts || !hydra.ibags || !hydra.imods || !hydra.igens || !hydra.shdrs) 1253 | { 1254 | //if (e) *e = TSF_INVALID_INCOMPLETE; 1255 | } 1256 | else if (fontSamples == TSF_NULL) 1257 | { 1258 | //if (e) *e = TSF_INVALID_NOSAMPLEDATA; 1259 | } 1260 | else 1261 | { 1262 | res = (tsf*)TSF_MALLOC(sizeof(tsf)); 1263 | TSF_MEMSET(res, 0, sizeof(tsf)); 1264 | res->presetNum = hydra.phdrNum - 1; 1265 | res->presets = (struct tsf_preset*)TSF_MALLOC(res->presetNum * sizeof(struct tsf_preset)); 1266 | res->fontSamples = fontSamples; 1267 | res->outSampleRate = 44100.0f; 1268 | fontSamples = TSF_NULL; //don't free below 1269 | tsf_load_presets(res, &hydra, fontSampleCount); 1270 | } 1271 | TSF_FREE(hydra.phdrs); TSF_FREE(hydra.pbags); TSF_FREE(hydra.pmods); 1272 | TSF_FREE(hydra.pgens); TSF_FREE(hydra.insts); TSF_FREE(hydra.ibags); 1273 | TSF_FREE(hydra.imods); TSF_FREE(hydra.igens); TSF_FREE(hydra.shdrs); 1274 | TSF_FREE(fontSamples); 1275 | return res; 1276 | } 1277 | 1278 | TSFDEF void tsf_close(tsf* f) 1279 | { 1280 | struct tsf_preset *preset, *presetEnd; 1281 | if (!f) return; 1282 | for (preset = f->presets, presetEnd = preset + f->presetNum; preset != presetEnd; preset++) 1283 | TSF_FREE(preset->regions); 1284 | TSF_FREE(f->presets); 1285 | TSF_FREE(f->fontSamples); 1286 | TSF_FREE(f->voices); 1287 | if (f->channels) { TSF_FREE(f->channels->channels); TSF_FREE(f->channels); } 1288 | TSF_FREE(f->outputSamples); 1289 | TSF_FREE(f); 1290 | } 1291 | 1292 | TSFDEF void tsf_reset(tsf* f) 1293 | { 1294 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1295 | for (; v != vEnd; v++) 1296 | if (v->playingPreset != -1 && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) 1297 | tsf_voice_endquick(f, v); 1298 | if (f->channels) { TSF_FREE(f->channels->channels); TSF_FREE(f->channels); f->channels = TSF_NULL; } 1299 | } 1300 | 1301 | TSFDEF int tsf_get_presetindex(const tsf* f, int bank, int preset_number) 1302 | { 1303 | const struct tsf_preset *presets; 1304 | int i, iMax; 1305 | for (presets = f->presets, i = 0, iMax = f->presetNum; i < iMax; i++) 1306 | if (presets[i].preset == preset_number && presets[i].bank == bank) 1307 | return i; 1308 | return -1; 1309 | } 1310 | 1311 | TSFDEF int tsf_get_presetcount(const tsf* f) 1312 | { 1313 | return f->presetNum; 1314 | } 1315 | 1316 | TSFDEF const char* tsf_get_presetname(const tsf* f, int preset) 1317 | { 1318 | return (preset < 0 || preset >= f->presetNum ? TSF_NULL : f->presets[preset].presetName); 1319 | } 1320 | 1321 | TSFDEF const char* tsf_bank_get_presetname(const tsf* f, int bank, int preset_number) 1322 | { 1323 | return tsf_get_presetname(f, tsf_get_presetindex(f, bank, preset_number)); 1324 | } 1325 | 1326 | TSFDEF void tsf_set_output(tsf* f, enum TSFOutputMode outputmode, int samplerate, float global_gain_db) 1327 | { 1328 | f->outputmode = outputmode; 1329 | f->outSampleRate = (float)(samplerate >= 1 ? samplerate : 44100.0f); 1330 | f->globalGainDB = global_gain_db; 1331 | } 1332 | 1333 | TSFDEF void tsf_set_volume(tsf* f, float global_volume) 1334 | { 1335 | f->globalGainDB = (global_volume == 1.0f ? 0 : -tsf_gainToDecibels(1.0f / global_volume)); 1336 | } 1337 | 1338 | TSFDEF void tsf_set_max_voices(tsf* f, int max_voices) 1339 | { 1340 | int i = f->voiceNum; 1341 | f->voiceNum = f->maxVoiceNum = (f->voiceNum > max_voices ? f->voiceNum : max_voices); 1342 | f->voices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice)); 1343 | for (; i != max_voices; i++) 1344 | f->voices[i].playingPreset = -1; 1345 | } 1346 | 1347 | TSFDEF void tsf_note_on(tsf* f, int preset_index, int key, float vel) 1348 | { 1349 | short midiVelocity = (short)(vel * 127); 1350 | int voicePlayIndex; 1351 | struct tsf_region *region, *regionEnd; 1352 | 1353 | if (preset_index < 0 || preset_index >= f->presetNum) return; 1354 | if (vel <= 0.0f) { tsf_note_off(f, preset_index, key); return; } 1355 | 1356 | // Play all matching regions. 1357 | voicePlayIndex = f->voicePlayIndex++; 1358 | for (region = f->presets[preset_index].regions, regionEnd = region + f->presets[preset_index].regionNum; region != regionEnd; region++) 1359 | { 1360 | struct tsf_voice *voice, *v, *vEnd; TSF_BOOL doLoop; float lowpassFilterQDB, lowpassFc; 1361 | if (key < region->lokey || key > region->hikey || midiVelocity < region->lovel || midiVelocity > region->hivel) continue; 1362 | 1363 | voice = TSF_NULL, v = f->voices, vEnd = v + f->voiceNum; 1364 | if (region->group) 1365 | { 1366 | for (; v != vEnd; v++) 1367 | if (v->playingPreset == preset_index && v->region->group == region->group) tsf_voice_endquick(f, v); 1368 | else if (v->playingPreset == -1 && !voice) voice = v; 1369 | } 1370 | else for (; v != vEnd; v++) if (v->playingPreset == -1) { voice = v; break; } 1371 | 1372 | if (!voice) 1373 | { 1374 | if (f->maxVoiceNum) 1375 | { 1376 | // voices have been pre-allocated and limited to a maximum, unable to start playing this voice 1377 | continue; 1378 | } 1379 | f->voiceNum += 4; 1380 | f->voices = (struct tsf_voice*)TSF_REALLOC(f->voices, f->voiceNum * sizeof(struct tsf_voice)); 1381 | voice = &f->voices[f->voiceNum - 4]; 1382 | voice[1].playingPreset = voice[2].playingPreset = voice[3].playingPreset = -1; 1383 | } 1384 | 1385 | voice->region = region; 1386 | voice->playingPreset = preset_index; 1387 | voice->playingKey = key; 1388 | voice->playIndex = voicePlayIndex; 1389 | voice->noteGainDB = f->globalGainDB - region->attenuation - tsf_gainToDecibels(1.0f / vel); 1390 | 1391 | if (f->channels) 1392 | { 1393 | f->channels->setupVoice(f, voice); 1394 | } 1395 | else 1396 | { 1397 | tsf_voice_calcpitchratio(voice, 0, f->outSampleRate); 1398 | // The SFZ spec is silent about the pan curve, but a 3dB pan law seems common. This sqrt() curve matches what Dimension LE does; Alchemy Free seems closer to sin(adjustedPan * pi/2). 1399 | voice->panFactorLeft = TSF_SQRTF(0.5f - region->pan); 1400 | voice->panFactorRight = TSF_SQRTF(0.5f + region->pan); 1401 | } 1402 | 1403 | // Offset/end. 1404 | voice->sourceSamplePosition = region->offset; 1405 | 1406 | // Loop. 1407 | doLoop = (region->loop_mode != TSF_LOOPMODE_NONE && region->loop_start < region->loop_end); 1408 | voice->loopStart = (doLoop ? region->loop_start : 0); 1409 | voice->loopEnd = (doLoop ? region->loop_end : 0); 1410 | 1411 | // Setup envelopes. 1412 | tsf_voice_envelope_setup(&voice->ampenv, ®ion->ampenv, key, midiVelocity, TSF_TRUE, f->outSampleRate); 1413 | tsf_voice_envelope_setup(&voice->modenv, ®ion->modenv, key, midiVelocity, TSF_FALSE, f->outSampleRate); 1414 | 1415 | // Setup lowpass filter. 1416 | lowpassFc = (region->initialFilterFc <= 13500 ? tsf_cents2Hertz((float)region->initialFilterFc) / f->outSampleRate : 1.0f); 1417 | lowpassFilterQDB = region->initialFilterQ / 10.0f; 1418 | voice->lowpass.QInv = 1.0 / TSF_POW(10.0, (lowpassFilterQDB / 20.0)); 1419 | voice->lowpass.z1 = voice->lowpass.z2 = 0; 1420 | voice->lowpass.active = (lowpassFc < 0.499f); 1421 | if (voice->lowpass.active) tsf_voice_lowpass_setup(&voice->lowpass, lowpassFc); 1422 | 1423 | // Setup LFO filters. 1424 | tsf_voice_lfo_setup(&voice->modlfo, region->delayModLFO, region->freqModLFO, f->outSampleRate); 1425 | tsf_voice_lfo_setup(&voice->viblfo, region->delayVibLFO, region->freqVibLFO, f->outSampleRate); 1426 | } 1427 | } 1428 | 1429 | TSFDEF int tsf_bank_note_on(tsf* f, int bank, int preset_number, int key, float vel) 1430 | { 1431 | int preset_index = tsf_get_presetindex(f, bank, preset_number); 1432 | if (preset_index == -1) return 0; 1433 | tsf_note_on(f, preset_index, key, vel); 1434 | return 1; 1435 | } 1436 | 1437 | TSFDEF void tsf_note_off(tsf* f, int preset_index, int key) 1438 | { 1439 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast = TSF_NULL; 1440 | for (; v != vEnd; v++) 1441 | { 1442 | //Find the first and last entry in the voices list with matching preset, key and look up the smallest play index 1443 | if (v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue; 1444 | else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; 1445 | else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; 1446 | } 1447 | if (!vMatchFirst) return; 1448 | for (v = vMatchFirst; v <= vMatchLast; v++) 1449 | { 1450 | //Stop all voices with matching preset, key and the smallest play index which was enumerated above 1451 | if (v != vMatchFirst && v != vMatchLast && 1452 | (v->playIndex != vMatchFirst->playIndex || v->playingPreset != preset_index || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; 1453 | tsf_voice_end(f, v); 1454 | } 1455 | } 1456 | 1457 | TSFDEF int tsf_bank_note_off(tsf* f, int bank, int preset_number, int key) 1458 | { 1459 | int preset_index = tsf_get_presetindex(f, bank, preset_number); 1460 | if (preset_index == -1) return 0; 1461 | tsf_note_off(f, preset_index, key); 1462 | return 1; 1463 | } 1464 | 1465 | TSFDEF void tsf_note_off_all(tsf* f) 1466 | { 1467 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1468 | for (; v != vEnd; v++) if (v->playingPreset != -1 && v->ampenv.segment < TSF_SEGMENT_RELEASE) 1469 | tsf_voice_end(f, v); 1470 | } 1471 | 1472 | TSFDEF int tsf_active_voice_count(tsf* f) 1473 | { 1474 | int count = 0; 1475 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1476 | for (; v != vEnd; v++) if (v->playingPreset != -1) count++; 1477 | return count; 1478 | } 1479 | 1480 | TSFDEF void tsf_render_short(tsf* f, short* buffer, int samples, int flag_mixing) 1481 | { 1482 | float *floatSamples; 1483 | int channelSamples = (f->outputmode == TSF_MONO ? 1 : 2) * samples, floatBufferSize = channelSamples * sizeof(float); 1484 | short* bufferEnd = buffer + channelSamples; 1485 | if (floatBufferSize > f->outputSampleSize) 1486 | { 1487 | TSF_FREE(f->outputSamples); 1488 | f->outputSamples = (float*)TSF_MALLOC(floatBufferSize); 1489 | f->outputSampleSize = floatBufferSize; 1490 | } 1491 | 1492 | tsf_render_float(f, f->outputSamples, samples, TSF_FALSE); 1493 | 1494 | floatSamples = f->outputSamples; 1495 | if (flag_mixing) 1496 | while (buffer != bufferEnd) 1497 | { 1498 | float v = *floatSamples++; 1499 | int vi = *buffer + (v < -1.00004566f ? (int)-32768 : (v > 1.00001514f ? (int)32767 : (int)(v * 32767.5f))); 1500 | *buffer++ = (vi < -32768 ? (short)-32768 : (vi > 32767 ? (short)32767 : (short)vi)); 1501 | } 1502 | else 1503 | while (buffer != bufferEnd) 1504 | { 1505 | float v = *floatSamples++; 1506 | *buffer++ = (v < -1.00004566f ? (short)-32768 : (v > 1.00001514f ? (short)32767 : (short)(v * 32767.5f))); 1507 | } 1508 | } 1509 | 1510 | TSFDEF void tsf_render_float(tsf* f, float* buffer, int samples, int flag_mixing) 1511 | { 1512 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1513 | if (!flag_mixing) TSF_MEMSET(buffer, 0, (f->outputmode == TSF_MONO ? 1 : 2) * sizeof(float) * samples); 1514 | for (; v != vEnd; v++) 1515 | if (v->playingPreset != -1) 1516 | tsf_voice_render(f, v, buffer, samples); 1517 | } 1518 | 1519 | static void tsf_channel_setup_voice(tsf* f, struct tsf_voice* v) 1520 | { 1521 | struct tsf_channel* c = &f->channels->channels[f->channels->activeChannel]; 1522 | float newpan = v->region->pan + c->panOffset; 1523 | v->playingChannel = f->channels->activeChannel; 1524 | v->noteGainDB += c->gainDB; 1525 | tsf_voice_calcpitchratio(v, (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)), f->outSampleRate); 1526 | if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } 1527 | else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } 1528 | else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } 1529 | } 1530 | 1531 | static struct tsf_channel* tsf_channel_init(tsf* f, int channel) 1532 | { 1533 | int i; 1534 | if (f->channels && channel < f->channels->channelNum) return &f->channels->channels[channel]; 1535 | if (!f->channels) 1536 | { 1537 | f->channels = (struct tsf_channels*)TSF_MALLOC(sizeof(struct tsf_channels)); 1538 | f->channels->setupVoice = &tsf_channel_setup_voice; 1539 | f->channels->channels = NULL; 1540 | f->channels->channelNum = 0; 1541 | f->channels->activeChannel = 0; 1542 | } 1543 | i = f->channels->channelNum; 1544 | f->channels->channelNum = channel + 1; 1545 | f->channels->channels = (struct tsf_channel*)TSF_REALLOC(f->channels->channels, f->channels->channelNum * sizeof(struct tsf_channel)); 1546 | for (; i <= channel; i++) 1547 | { 1548 | struct tsf_channel* c = &f->channels->channels[i]; 1549 | c->presetIndex = c->bank = 0; 1550 | c->pitchWheel = c->midiPan = 8192; 1551 | c->midiVolume = c->midiExpression = 16383; 1552 | c->midiRPN = 0xFFFF; 1553 | c->midiData = 0; 1554 | c->panOffset = 0.0f; 1555 | c->gainDB = 0.0f; 1556 | c->pitchRange = 2.0f; 1557 | c->tuning = 0.0f; 1558 | } 1559 | return &f->channels->channels[channel]; 1560 | } 1561 | 1562 | static void tsf_channel_applypitch(tsf* f, int channel, struct tsf_channel* c) 1563 | { 1564 | struct tsf_voice *v, *vEnd; 1565 | float pitchShift = (c->pitchWheel == 8192 ? c->tuning : ((c->pitchWheel / 16383.0f * c->pitchRange * 2.0f) - c->pitchRange + c->tuning)); 1566 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) 1567 | if (v->playingChannel == channel && v->playingPreset != -1) 1568 | tsf_voice_calcpitchratio(v, pitchShift, f->outSampleRate); 1569 | } 1570 | 1571 | TSFDEF void tsf_channel_set_presetindex(tsf* f, int channel, int preset_index) 1572 | { 1573 | tsf_channel_init(f, channel)->presetIndex = (unsigned short)preset_index; 1574 | } 1575 | 1576 | TSFDEF int tsf_channel_set_presetnumber(tsf* f, int channel, int preset_number, int flag_mididrums) 1577 | { 1578 | struct tsf_channel *c = tsf_channel_init(f, channel); 1579 | int preset_index; 1580 | if (flag_mididrums) 1581 | { 1582 | preset_index = tsf_get_presetindex(f, 128 | (c->bank & 0x7FFF), preset_number); 1583 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, preset_number); 1584 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 128, 0); 1585 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); 1586 | } 1587 | else preset_index = tsf_get_presetindex(f, (c->bank & 0x7FFF), preset_number); 1588 | if (preset_index == -1) preset_index = tsf_get_presetindex(f, 0, preset_number); 1589 | if (preset_index != -1) 1590 | { 1591 | c->presetIndex = (unsigned short)preset_index; 1592 | return 1; 1593 | } 1594 | return 0; 1595 | } 1596 | 1597 | TSFDEF void tsf_channel_set_bank(tsf* f, int channel, int bank) 1598 | { 1599 | tsf_channel_init(f, channel)->bank = (unsigned short)bank; 1600 | } 1601 | 1602 | TSFDEF int tsf_channel_set_bank_preset(tsf* f, int channel, int bank, int preset_number) 1603 | { 1604 | struct tsf_channel *c = tsf_channel_init(f, channel); 1605 | int preset_index = tsf_get_presetindex(f, bank, preset_number); 1606 | if (preset_index == -1) return 0; 1607 | c->presetIndex = (unsigned short)preset_index; 1608 | c->bank = (unsigned short)bank; 1609 | return 1; 1610 | } 1611 | 1612 | TSFDEF void tsf_channel_set_pan(tsf* f, int channel, float pan) 1613 | { 1614 | struct tsf_voice *v, *vEnd; 1615 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) 1616 | if (v->playingChannel == channel && v->playingPreset != -1) 1617 | { 1618 | float newpan = v->region->pan + pan - 0.5f; 1619 | if (newpan <= -0.5f) { v->panFactorLeft = 1.0f; v->panFactorRight = 0.0f; } 1620 | else if (newpan >= 0.5f) { v->panFactorLeft = 0.0f; v->panFactorRight = 1.0f; } 1621 | else { v->panFactorLeft = TSF_SQRTF(0.5f - newpan); v->panFactorRight = TSF_SQRTF(0.5f + newpan); } 1622 | } 1623 | tsf_channel_init(f, channel)->panOffset = pan - 0.5f; 1624 | } 1625 | 1626 | TSFDEF void tsf_channel_set_volume(tsf* f, int channel, float volume) 1627 | { 1628 | struct tsf_channel *c = tsf_channel_init(f, channel); 1629 | float gainDB = tsf_gainToDecibels(volume), gainDBChange = gainDB - c->gainDB; 1630 | struct tsf_voice *v, *vEnd; 1631 | if (gainDBChange == 0) return; 1632 | for (v = f->voices, vEnd = v + f->voiceNum; v != vEnd; v++) 1633 | if (v->playingChannel == channel && v->playingPreset != -1) 1634 | v->noteGainDB += gainDBChange; 1635 | c->gainDB = gainDB; 1636 | } 1637 | 1638 | TSFDEF void tsf_channel_set_pitchwheel(tsf* f, int channel, int pitch_wheel) 1639 | { 1640 | struct tsf_channel *c = tsf_channel_init(f, channel); 1641 | if (c->pitchWheel == pitch_wheel) return; 1642 | c->pitchWheel = (unsigned short)pitch_wheel; 1643 | tsf_channel_applypitch(f, channel, c); 1644 | } 1645 | 1646 | TSFDEF void tsf_channel_set_pitchrange(tsf* f, int channel, float pitch_range) 1647 | { 1648 | struct tsf_channel *c = tsf_channel_init(f, channel); 1649 | if (c->pitchRange == pitch_range) return; 1650 | c->pitchRange = pitch_range; 1651 | if (c->pitchWheel != 8192) tsf_channel_applypitch(f, channel, c); 1652 | } 1653 | 1654 | TSFDEF void tsf_channel_set_tuning(tsf* f, int channel, float tuning) 1655 | { 1656 | struct tsf_channel *c = tsf_channel_init(f, channel); 1657 | if (c->tuning == tuning) return; 1658 | c->tuning = tuning; 1659 | tsf_channel_applypitch(f, channel, c); 1660 | } 1661 | 1662 | TSFDEF void tsf_channel_note_on(tsf* f, int channel, int key, float vel) 1663 | { 1664 | if (!f->channels || channel >= f->channels->channelNum) return; 1665 | f->channels->activeChannel = channel; 1666 | tsf_note_on(f, f->channels->channels[channel].presetIndex, key, vel); 1667 | } 1668 | 1669 | TSFDEF void tsf_channel_note_off(tsf* f, int channel, int key) 1670 | { 1671 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum, *vMatchFirst = TSF_NULL, *vMatchLast = TSF_NULL; 1672 | for (; v != vEnd; v++) 1673 | { 1674 | //Find the first and last entry in the voices list with matching channel, key and look up the smallest play index 1675 | if (v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE) continue; 1676 | else if (!vMatchFirst || v->playIndex < vMatchFirst->playIndex) vMatchFirst = vMatchLast = v; 1677 | else if (v->playIndex == vMatchFirst->playIndex) vMatchLast = v; 1678 | } 1679 | if (!vMatchFirst) return; 1680 | for (v = vMatchFirst; v <= vMatchLast; v++) 1681 | { 1682 | //Stop all voices with matching channel, key and the smallest play index which was enumerated above 1683 | if (v != vMatchFirst && v != vMatchLast && 1684 | (v->playIndex != vMatchFirst->playIndex || v->playingPreset == -1 || v->playingChannel != channel || v->playingKey != key || v->ampenv.segment >= TSF_SEGMENT_RELEASE)) continue; 1685 | tsf_voice_end(f, v); 1686 | } 1687 | } 1688 | 1689 | TSFDEF void tsf_channel_note_off_all(tsf* f, int channel) 1690 | { 1691 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1692 | for (; v != vEnd; v++) 1693 | if (v->playingPreset != -1 && v->playingChannel == channel && v->ampenv.segment < TSF_SEGMENT_RELEASE) 1694 | tsf_voice_end(f, v); 1695 | } 1696 | 1697 | TSFDEF void tsf_channel_sounds_off_all(tsf* f, int channel) 1698 | { 1699 | struct tsf_voice *v = f->voices, *vEnd = v + f->voiceNum; 1700 | for (; v != vEnd; v++) 1701 | if (v->playingPreset != -1 && v->playingChannel == channel && (v->ampenv.segment < TSF_SEGMENT_RELEASE || v->ampenv.parameters.release)) 1702 | tsf_voice_endquick(f, v); 1703 | } 1704 | 1705 | TSFDEF void tsf_channel_midi_control(tsf* f, int channel, int controller, int control_value) 1706 | { 1707 | struct tsf_channel* c = tsf_channel_init(f, channel); 1708 | switch (controller) 1709 | { 1710 | case 7 /*VOLUME_MSB*/ : c->midiVolume = (unsigned short)((c->midiVolume & 0x7F ) | (control_value << 7)); goto TCMC_SET_VOLUME; 1711 | case 39 /*VOLUME_LSB*/ : c->midiVolume = (unsigned short)((c->midiVolume & 0x3F80) | control_value); goto TCMC_SET_VOLUME; 1712 | case 11 /*EXPRESSION_MSB*/ : c->midiExpression = (unsigned short)((c->midiExpression & 0x7F ) | (control_value << 7)); goto TCMC_SET_VOLUME; 1713 | case 43 /*EXPRESSION_LSB*/ : c->midiExpression = (unsigned short)((c->midiExpression & 0x3F80) | control_value); goto TCMC_SET_VOLUME; 1714 | case 10 /*PAN_MSB*/ : c->midiPan = (unsigned short)((c->midiPan & 0x7F ) | (control_value << 7)); goto TCMC_SET_PAN; 1715 | case 42 /*PAN_LSB*/ : c->midiPan = (unsigned short)((c->midiPan & 0x3F80) | control_value); goto TCMC_SET_PAN; 1716 | case 6 /*DATA_ENTRY_MSB*/ : c->midiData = (unsigned short)((c->midiData & 0x7F) | (control_value << 7)); goto TCMC_SET_DATA; 1717 | case 38 /*DATA_ENTRY_LSB*/ : c->midiData = (unsigned short)((c->midiData & 0x3F80) | control_value); goto TCMC_SET_DATA; 1718 | case 0 /*BANK_SELECT_MSB*/ : c->bank = (unsigned short)(0x8000 | control_value); return; //bank select MSB alone acts like LSB 1719 | case 32 /*BANK_SELECT_LSB*/ : c->bank = (unsigned short)((c->bank & 0x8000 ? ((c->bank & 0x7F) << 7) : 0) | control_value); return; 1720 | case 101 /*RPN_MSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x7F ) | (control_value << 7)); return; 1721 | case 100 /*RPN_LSB*/ : c->midiRPN = (unsigned short)(((c->midiRPN == 0xFFFF ? 0 : c->midiRPN) & 0x3F80) | control_value); return; 1722 | case 98 /*NRPN_LSB*/ : c->midiRPN = 0xFFFF; return; 1723 | case 99 /*NRPN_MSB*/ : c->midiRPN = 0xFFFF; return; 1724 | case 120 /*ALL_SOUND_OFF*/ : tsf_channel_sounds_off_all(f, channel); return; 1725 | case 123 /*ALL_NOTES_OFF*/ : tsf_channel_note_off_all(f, channel); return; 1726 | case 121 /*ALL_CTRL_OFF*/ : 1727 | c->midiVolume = c->midiExpression = 16383; 1728 | c->midiPan = 8192; 1729 | c->bank = 0; 1730 | tsf_channel_set_volume(f, channel, 1.0f); 1731 | tsf_channel_set_pan(f, channel, 0.5f); 1732 | tsf_channel_set_pitchrange(f, channel, 2.0f); 1733 | return; 1734 | } 1735 | return; 1736 | TCMC_SET_VOLUME: 1737 | //Raising to the power of 3 seems to result in a decent sounding volume curve for MIDI 1738 | tsf_channel_set_volume(f, channel, TSF_POWF((c->midiVolume / 16383.0f) * (c->midiExpression / 16383.0f), 3.0f)); 1739 | return; 1740 | TCMC_SET_PAN: 1741 | tsf_channel_set_pan(f, channel, c->midiPan / 16383.0f); 1742 | return; 1743 | TCMC_SET_DATA: 1744 | if (c->midiRPN == 0) tsf_channel_set_pitchrange(f, channel, (c->midiData >> 7) + 0.01f * (c->midiData & 0x7F)); 1745 | else if (c->midiRPN == 1) tsf_channel_set_tuning(f, channel, (int)c->tuning + ((float)c->midiData - 8192.0f) / 8192.0f); //fine tune 1746 | else if (c->midiRPN == 2 && controller == 6) tsf_channel_set_tuning(f, channel, ((float)control_value - 64.0f) + (c->tuning - (int)c->tuning)); //coarse tune 1747 | return; 1748 | } 1749 | 1750 | TSFDEF int tsf_channel_get_preset_index(tsf* f, int channel) 1751 | { 1752 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].presetIndex : 0); 1753 | } 1754 | 1755 | TSFDEF int tsf_channel_get_preset_bank(tsf* f, int channel) 1756 | { 1757 | return (f->channels && channel < f->channels->channelNum ? (f->channels->channels[channel].bank & 0x7FFF) : 0); 1758 | } 1759 | 1760 | TSFDEF int tsf_channel_get_preset_number(tsf* f, int channel) 1761 | { 1762 | return (f->channels && channel < f->channels->channelNum ? f->presets[f->channels->channels[channel].presetIndex].preset : 0); 1763 | } 1764 | 1765 | TSFDEF float tsf_channel_get_pan(tsf* f, int channel) 1766 | { 1767 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].panOffset - 0.5f : 0.5f); 1768 | } 1769 | 1770 | TSFDEF float tsf_channel_get_volume(tsf* f, int channel) 1771 | { 1772 | return (f->channels && channel < f->channels->channelNum ? tsf_decibelsToGain(f->channels->channels[channel].gainDB) : 1.0f); 1773 | } 1774 | 1775 | TSFDEF int tsf_channel_get_pitchwheel(tsf* f, int channel) 1776 | { 1777 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchWheel : 8192); 1778 | } 1779 | 1780 | TSFDEF float tsf_channel_get_pitchrange(tsf* f, int channel) 1781 | { 1782 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].pitchRange : 2.0f); 1783 | } 1784 | 1785 | TSFDEF float tsf_channel_get_tuning(tsf* f, int channel) 1786 | { 1787 | return (f->channels && channel < f->channels->channelNum ? f->channels->channels[channel].tuning : 0.0f); 1788 | } 1789 | 1790 | #ifdef __cplusplus 1791 | } 1792 | #endif 1793 | 1794 | #endif //TSF_IMPLEMENTATION 1795 | --------------------------------------------------------------------------------