├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── README.md
├── LICENSE
├── sfizz-node.js
├── .github
└── workflows
│ └── build.yml
├── index.css
├── index.html
├── sfizz-processor.js
├── util
└── WASMAudioBuffer.js
├── sfizz_webaudio.cpp
├── index.js
└── ace
└── mode-sfz.js
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "sfizz"]
2 | path = sfizz
3 | url = https://github.com/paulfd/sfizz
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(sfizz-webaudio)
2 |
3 | set(SFIZZ_TESTS OFF CACHE BOOL "" FORCE)
4 | set(SFIZZ_LV2 OFF CACHE BOOL "" FORCE)
5 | set(SFIZZ_LV2_UI OFF CACHE BOOL "" FORCE)
6 | set(SFIZZ_VST OFF CACHE BOOL "" FORCE)
7 | set(SFIZZ_RENDER OFF CACHE BOOL "" FORCE)
8 | set(SFIZZ_JACK OFF CACHE BOOL "" FORCE)
9 | add_subdirectory(sfizz)
10 |
11 | add_executable(sfizz-webaudio)
12 | target_sources(sfizz-webaudio PRIVATE sfizz_webaudio.cpp)
13 | target_link_libraries(sfizz-webaudio PRIVATE sfizz::sfizz)
14 | set_target_properties(sfizz-webaudio PROPERTIES
15 | LINK_FLAGS "--bind -s ENVIRONMENT=web -s ALLOW_MEMORY_GROWTH=1 -s SINGLE_FILE=1 -s WASM=1 -s WASM_ASYNC_COMPILATION=0 -s EXPORTED_FUNCTIONS=\"['_malloc']\" -s EXPORT_ES6=1"
16 | OUTPUT_NAME sfizz.wasm
17 | )
18 |
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## sfizz WebAudio/WebMidi demo
2 |
3 | Live demo (requires Chrome or one of its derivatives): https://sfz.tools/sfizz-webaudio/
4 |
5 | This repository contains a HTML/Javascript/WebAssembly front-end for [sfizz](https://sfz.tools/sfizz), which allows prototyping of virtual instruments in the SFZ format.
6 | Right now only generators and embedded sample files are supported. This uses the `emscripten` branch off my sfizz's fork, available [here](https://github.com/paulfd/sfizz/tree/emscripten).
7 | Compared the main sfizz branch, the background loader is deactivated and all files are loaded in memory (since WebAssembly through browsers only allows access to a virtual file system).
8 |
9 | ### Building
10 |
11 | This assumes you have the [emsdk](https://github.com/emscripten-core/emsdk) installed and activated.
12 | ```sh
13 | mkdir build
14 | cd build
15 | emcmake cmake -DCMAKE_BUILD_TYPE=Release ..
16 | make -j
17 | ```
18 |
19 | From the `build` directory, you may then host the result on `localhost:8000` using python as
20 | ```sh
21 | python3 -m http.server --directory ..
22 | ```
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Paul Ferrand
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/sfizz-node.js:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Paul Ferrand
2 |
3 | // Permission is hereby granted, free of charge, to any person obtaining a
4 | // copy of this software and associated documentation files (the "Software"),
5 | // to deal in the Software without restriction, including without limitation
6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and/or sell copies of the Software, and to permit persons to whom the
8 | // Software is furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 | // DEALINGS IN THE SOFTWARE.
20 |
21 | export default class SfizzNode extends AudioWorkletNode {
22 | constructor(context) {
23 | super(context, 'sfizz', { numberOfOutputs: 2 });
24 | }
25 | }
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches:
6 | - '*'
7 |
8 | env:
9 | BUILD_TYPE: Release
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | with:
17 | submodules: recursive
18 | - uses: mymindstorm/setup-emsdk@v11
19 | - name: Create Build Environment
20 | shell: bash
21 | working-directory: ${{runner.workspace}}/sfizz-webaudio
22 | run: cmake -E make_directory build
23 | - name: Configure CMake
24 | shell: bash
25 | working-directory: ${{runner.workspace}}/sfizz-webaudio/build
26 | run: |
27 | emcmake cmake "$GITHUB_WORKSPACE" -DCMAKE_BUILD_TYPE=Release
28 | - name: Build
29 | shell: bash
30 | working-directory: ${{runner.workspace}}/sfizz-webaudio/build
31 | run: |
32 | cmake --build . --config "$BUILD_TYPE" -j 2
33 | - name: Inspect directory
34 | working-directory: ${{runner.workspace}}/sfizz-webaudio
35 | run: |
36 | pwd
37 | ls -al
38 | ls -al build
39 | - name: Archive artifacts
40 | uses: actions/upload-artifact@v2
41 | with:
42 | name: sfizz-webaudio
43 | path: |
44 | index.html
45 | index.js
46 | index.css
47 | sfizz-node.js
48 | sfizz-processor.js
49 | build/sfizz.wasm.js
50 | util/WASMAudioBuffer.js
51 |
52 | upload:
53 | name: Create release and upload artifacts
54 | needs:
55 | - build
56 | runs-on: ubuntu-latest
57 | steps:
58 | - name: Download artifacts
59 | uses: actions/download-artifact@v2
60 | - name: Inspect directory
61 | run: ls -alFR
62 | - name: Upload to GitHub pages
63 | uses: peaceiris/actions-gh-pages@v3
64 | with:
65 | github_token: ${{ secrets.GITHUB_TOKEN }}
66 | publish_branch: www
67 | publish_dir: ./sfizz-webaudio
68 | force_orphan: true
69 | - name: Rezip artifact
70 | uses: montudor/action-zip@v1
71 | with:
72 | args: zip -r sfizz-webaudio.zip sfizz-webaudio
73 | - name: Create release and upload artifacts
74 | env:
75 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
76 | run: |
77 | wget -q https://github.com/TheAssassin/pyuploadtool/releases/download/continuous/pyuploadtool-x86_64.AppImage
78 | chmod +x pyuploadtool-x86_64.AppImage
79 | ./pyuploadtool-x86_64.AppImage sfizz-webaudio.zip
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | #control-row {
2 | display: flex;
3 | flex-direction: row;
4 | /* justify-content: center; */
5 | width: 100%;
6 | padding: 5px;
7 | }
8 |
9 | #control-row > div {
10 | margin: 5px;
11 | }
12 |
13 | #left-control-column {
14 | display: flex;
15 | flex-direction: column;
16 | width: fit-content;
17 | }
18 |
19 | #left-control-column .ui.slider {
20 | width: 150px;
21 | }
22 |
23 | #left-control-column .ui.input {
24 | width: 80px;
25 | height: 30px;
26 | margin-left: 10px;
27 | }
28 |
29 | #left-control-column > div {
30 | width: fit-content;
31 | display: flex;
32 | flex-direction: row;
33 | align-items: center;
34 | }
35 |
36 | #left-control-column > div > span {
37 | width: 100px;
38 | }
39 |
40 | #right-control-column {
41 | display: flex;
42 | flex-direction: column;
43 | width: fit-content;
44 | }
45 |
46 | #right-control-column > div > span {
47 | position: relative;
48 | right: 0;
49 | left: auto;
50 | }
51 |
52 | #right-control-column > div {
53 | width:150px;
54 | margin-top: 5px;
55 | }
56 |
57 | #keyboard-buttons {
58 | position: relative;
59 | width: fit-content;
60 | margin-left: auto;
61 | margin-right: auto;
62 | }
63 |
64 | #keyboard-buttons > span {
65 | padding: 5px;
66 | }
67 |
68 | #keyboard {
69 | position: relative;
70 | margin-left: auto;
71 | margin-right: auto;
72 | margin-top: 10;
73 | height: 100px;
74 | }
75 |
76 | #editor {
77 | flex: 1;
78 | height: 500px;
79 | }
80 |
81 | .white {
82 | width: 20px;
83 | height: 100px;
84 | position: absolute;
85 | top: 0;
86 | border-right-style: none;
87 | border-width: 1px;
88 | border-color: black;
89 | background: white;
90 | display: flex;
91 | justify-content: center;
92 | color: black;
93 | align-items: flex-end;
94 | }
95 |
96 | .black {
97 | width: 10px;
98 | height: 60px;
99 | position: absolute;
100 | border-color: black;
101 | border-width: 1px;
102 | background: black;
103 | display: flex;
104 | justify-content: center;
105 | align-items: flex-end;
106 | color: white;
107 | }
108 |
109 | .black:hover {
110 | background-color: dimgrey;
111 | }
112 |
113 | .white:hover {
114 | background-color: lightgray;
115 | }
116 |
117 | #overlay {
118 | background: rgba(0, 0, 0, .9);
119 | color: #fff;
120 | cursor: pointer;
121 | width: 100%;
122 | height: 100%;
123 | z-index: 10;
124 | top: 0;
125 | left: 0;
126 | position: fixed;
127 | display: flex;
128 | justify-content: center;
129 | align-items: center;
130 | }
131 |
132 | .overlay-text {
133 | width: fit-content;
134 | margin: auto;
135 | z-index: 11;
136 | }
137 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
Click to enable WebAudio
29 |
30 |
31 |
32 |
33 |
// You can use Ctrl + S to reload
<region> sample=*saw
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | Octave
45 |
46 |
47 |
48 |
49 |
50 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
65 |
--------------------------------------------------------------------------------
/sfizz-processor.js:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Paul Ferrand
2 |
3 | // Permission is hereby granted, free of charge, to any person obtaining a
4 | // copy of this software and associated documentation files (the "Software"),
5 | // to deal in the Software without restriction, including without limitation
6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and/or sell copies of the Software, and to permit persons to whom the
8 | // Software is furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 | // DEALINGS IN THE SOFTWARE.
20 |
21 | import Module from './build/sfizz.wasm.js';
22 | import WASMAudioBuffer from './util/WASMAudioBuffer.js';
23 |
24 | const SfizzModule = new Module();
25 |
26 | // Web Audio API's render block size
27 | const NUM_FRAMES = 128;
28 |
29 | class SfizzProcessor extends AudioWorkletProcessor {
30 | constructor() {
31 | super();
32 | // Create an instance of Synthesizer and WASM memory helper. Then set up an
33 | // event handler for MIDI data from the main thread.
34 | this._synth = new SfizzModule.SfizzWrapper(sampleRate);
35 | this._leftBuffer = new WASMAudioBuffer(SfizzModule, NUM_FRAMES, 1, 1);
36 | this._rightBuffer = new WASMAudioBuffer(SfizzModule, NUM_FRAMES, 1, 1);
37 | this._activeVoices = 0;
38 | this.port.onmessage = this._handleMessage.bind(this);
39 | }
40 |
41 | process(inputs, outputs) {
42 | // Call the render function to fill the WASM buffer. Then clone the
43 | // rendered data to process() callback's output buffer.
44 | this._synth.render(this._leftBuffer.getPointer(), this._rightBuffer.getPointer(), NUM_FRAMES);
45 | outputs[0][0].set(this._leftBuffer.getF32Array());
46 | outputs[1][0].set(this._rightBuffer.getF32Array());
47 | const activeVoices = this._synth.numActiveVoices();
48 | if (activeVoices != this._activeVoices) {
49 | this.port.postMessage({ activeVoices: this._synth.numActiveVoices() });
50 | this._activeVoices = activeVoices;
51 | }
52 | return true;
53 | }
54 |
55 | _handleMessage(event) {
56 | const data = event.data;
57 | switch (data.type) {
58 | case 'note_on':
59 | this._synth.noteOn(0, data.number, data.value);
60 | break;
61 | case 'note_off':
62 | this._synth.noteOff(0, data.number, data.value);
63 | break;
64 | case 'cc':
65 | this._synth.cc(0, data.number, data.value);
66 | break;
67 | case 'aftertouch':
68 | this._synth.aftertouch(0, data.value);
69 | break;
70 | case 'pitch_wheel':
71 | this._synth.pitchWheel(0, data.value);
72 | break;
73 | case 'text':
74 | this._synth.load(data.sfz);
75 | const usedCCs = this._synth.usedCCs();
76 | for (let i = 0; i < usedCCs.size(); i++) {
77 | const cc = usedCCs.get(i);
78 | var ccLabel = this._synth.ccLabel(cc);
79 | // Default names
80 | if (ccLabel == '') {
81 | switch(cc) {
82 | case 7: ccLabel = 'Volume'; break;
83 | case 10: ccLabel = 'Pan'; break;
84 | case 11: ccLabel = 'Expression'; break;
85 | }
86 | }
87 |
88 | const ccValue = this._synth.ccValue(cc);
89 | const ccDefault = this._synth.ccDefault(cc);
90 | this.port.postMessage({ cc: cc, label: ccLabel, value: ccValue, default: ccDefault });
91 | }
92 | this.port.postMessage({ numRegions: this._synth.numRegions() });
93 | break;
94 | case 'num_regions':
95 | this.port.postMessage({ numRegions: this._synth.numRegions() });
96 | break;
97 | case 'active_voices':
98 | this.port.postMessage({ activeVoices: this._synth.numActiveVoices() });
99 | break;
100 | default:
101 | console.log("Unknown message: ", event);
102 | }
103 | }
104 | }
105 |
106 | registerProcessor('sfizz', SfizzProcessor);
--------------------------------------------------------------------------------
/util/WASMAudioBuffer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 Google Inc. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 |
18 | // Basic byte unit of WASM heap. (16 bit = 2 bytes)
19 | const BYTES_PER_UNIT = Uint16Array.BYTES_PER_ELEMENT;
20 |
21 | // Byte per audio sample. (32 bit float)
22 | const BYTES_PER_SAMPLE = Float32Array.BYTES_PER_ELEMENT;
23 |
24 | // The max audio channel on Chrome is 32.
25 | const MAX_CHANNEL_COUNT = 32;
26 |
27 | /**
28 | * A WASM HEAP wrapper for AudioBuffer class. This breaks down the AudioBuffer
29 | * into an Array of Float32Array for the convinient WASM opearion.
30 | *
31 | * @class
32 | * @dependency Module A WASM module generated by the emscripten glue code.
33 | */
34 | class WASMAudioBuffer {
35 | /**
36 | * @constructor
37 | * @param {object} wasmModule WASM module generated by Emscripten.
38 | * @param {number} length Buffer frame length.
39 | * @param {number} channelCount Number of channels.
40 | * @param {number=} maxChannelCount Maximum number of channels.
41 | */
42 | constructor(wasmModule, length, channelCount, maxChannelCount) {
43 | // The |channelCount| must be greater than 0, and less than or equal to
44 | // the maximum channel count.
45 | this._isInitialized = false;
46 | this._module = wasmModule;
47 | this._length = length;
48 | this._maxChannelCount = maxChannelCount
49 | ? Math.min(maxChannelCount, MAX_CHANNEL_COUNT)
50 | : channelCount;
51 | this._channelCount = channelCount;
52 | this._allocateHeap();
53 | this._isInitialized = true;
54 | }
55 |
56 | /**
57 | * Allocates memory in the WASM heap and set up Float32Array views for the
58 | * channel data.
59 | *
60 | * @private
61 | */
62 | _allocateHeap() {
63 | const channelByteSize = this._length * BYTES_PER_SAMPLE;
64 | const dataByteSize = this._channelCount * channelByteSize;
65 | this._dataPtr = this._module._malloc(dataByteSize);
66 | this._channelData = [];
67 | for (let i = 0; i < this._channelCount; ++i) {
68 | let startByteOffset = this._dataPtr + i * channelByteSize;
69 | let endByteOffset = startByteOffset + channelByteSize;
70 | // Get the actual array index by dividing the byte offset by 2 bytes.
71 | this._channelData[i] =
72 | this._module.HEAPF32.subarray(startByteOffset >> BYTES_PER_UNIT,
73 | endByteOffset >> BYTES_PER_UNIT);
74 | }
75 | }
76 |
77 | /**
78 | * Adapt the current channel count to the new input buffer.
79 | *
80 | * @param {number} newChannelCount The new channel count.
81 | */
82 | adaptChannel(newChannelCount) {
83 | if (newChannelCount < this._maxChannelCount) {
84 | this._channelCount = newChannelCount;
85 | }
86 | }
87 |
88 | /**
89 | * Getter for the buffer length in frames.
90 | *
91 | * @return {?number} Buffer length in frames.
92 | */
93 | get length() {
94 | return this._isInitialized ? this._length : null;
95 | }
96 |
97 | /**
98 | * Getter for the number of channels.
99 | *
100 | * @return {?number} Buffer length in frames.
101 | */
102 | get numberOfChannels() {
103 | return this._isInitialized ? this._channelCount : null;
104 | }
105 |
106 | /**
107 | * Getter for the maxixmum number of channels allowed for the instance.
108 | *
109 | * @return {?number} Buffer length in frames.
110 | */
111 | get maxChannelCount() {
112 | return this._isInitialized ? this._maxChannelCount : null;
113 | }
114 |
115 | /**
116 | * Returns a Float32Array object for a given channel index. If the channel
117 | * index is undefined, it returns the reference to the entire array of channel
118 | * data.
119 | *
120 | * @param {number|undefined} channelIndex Channel index.
121 | * @return {?Array} a channel data array or an
122 | * array of channel data.
123 | */
124 | getChannelData(channelIndex) {
125 | if (channelIndex >= this._channelCount) {
126 | return null;
127 | }
128 |
129 | return typeof channelIndex === 'undefined'
130 | ? this._channelData : this._channelData[channelIndex];
131 | }
132 |
133 | getF32Array() {
134 | return this._channelData[0];
135 | }
136 |
137 | /**
138 | * Returns the base address of the allocated memory space in the WASM heap.
139 | *
140 | * @return {number} WASM Heap address.
141 | */
142 | getPointer() {
143 | return this._dataPtr;
144 | }
145 |
146 | /**
147 | * Frees the allocated memory space in the WASM heap.
148 | */
149 | free() {
150 | this._isInitialized = false;
151 | this._module._free(this._dataPtr);
152 | this._module._free(this._pointerArrayPtr); // Useful?
153 | this._channelData = null;
154 | }
155 | } // class WASMAudioBuffer
156 |
157 | export default WASMAudioBuffer;
158 |
--------------------------------------------------------------------------------
/sfizz_webaudio.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Paul Ferrand
2 |
3 | // Permission is hereby granted, free of charge, to any person obtaining a
4 | // copy of this software and associated documentation files (the "Software"),
5 | // to deal in the Software without restriction, including without limitation
6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and/or sell copies of the Software, and to permit persons to whom the
8 | // Software is furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 | // DEALINGS IN THE SOFTWARE.
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include "sfizz.hpp"
26 | #include "sfizz/utility/bit_array/BitArray.h"
27 |
28 | using namespace emscripten;
29 |
30 | class SfizzWrapper
31 | {
32 | public:
33 | SfizzWrapper(int32_t sampleRate)
34 | {
35 | synth.setReceiveCallback(*client, &SfizzWrapper::staticCallback);
36 | synth.setSampleRate(static_cast(sampleRate));
37 | load(" sample=*saw");
38 | }
39 |
40 | void load(std::string file)
41 | {
42 | synth.loadSfzString("file.sfz", file);
43 | synth.getCCLabels();
44 | }
45 |
46 | void noteOn(int delay, uint8_t number, float value) { synth.hdNoteOn(delay, number, value); }
47 | void noteOff(int delay, uint8_t number, float value) { synth.hdNoteOff(delay, number, value); }
48 | void cc(int delay, uint8_t number, float value) { synth.hdcc(delay, number, value); }
49 | void aftertouch(int delay, float value) { synth.hdChannelAftertouch(delay, value); }
50 | void pitchWheel(int delay, float value) { synth.hdPitchWheel(delay, value); }
51 |
52 | int numRegions() const { return synth.getNumRegions(); }
53 | int numActiveVoices() const { return synth.getNumActiveVoices(); }
54 | std::vector usedCCs() const
55 | {
56 | const_cast(this)->synth.sendMessage(*client, 0, "/cc/slots", "", nullptr);
57 | return usedCCs_;
58 | }
59 | std::string ccLabel(int number) const
60 | {
61 | if (number < 0)
62 | return {};
63 |
64 | lastLabel.clear();
65 | std::stringstream path;
66 | path << "/cc" << number << "/label";
67 | const_cast(this)->synth.sendMessage(*client, 0, path.str().c_str(), "", nullptr);
68 | return lastLabel;
69 | }
70 | float ccValue(int number) const
71 | {
72 | if (number < 0)
73 | return 0.0f;
74 |
75 | lastCCValue = 0.0f;
76 | std::stringstream path;
77 | path << "/cc" << number << "/value";
78 | const_cast(this)->synth.sendMessage(*client, 0, path.str().c_str(), "", nullptr);
79 | return lastCCValue;
80 | }
81 |
82 | float ccDefault(int number) const
83 | {
84 | if (number < 0)
85 | return 0.0f;
86 |
87 | lastCCDefault = 0.0f;
88 | std::stringstream path;
89 | path << "/cc" << number << "/default";
90 | const_cast(this)->synth.sendMessage(*client, 0, path.str().c_str(), "", nullptr);
91 | return lastCCDefault;
92 | }
93 |
94 | void render(uintptr_t left, uintptr_t right, int32_t numFrames)
95 | {
96 | // Use type cast to hide the raw pointer in function arguments.
97 | float *output_array[2] = {
98 | reinterpret_cast(left),
99 | reinterpret_cast(right)};
100 | synth.renderBlock(output_array, numFrames);
101 | }
102 |
103 | static void staticCallback(void *data, int delay, const char *path, const char *sig, const sfizz_arg_t *args)
104 | {
105 | auto self = reinterpret_cast(data);
106 | self->clientCallback(delay, path, sig, args);
107 | }
108 |
109 | void clientCallback(int delay, const char *path, const char *sig, const sfizz_arg_t *args)
110 | {
111 | unsigned indices[8];
112 |
113 | if (!strcmp(path, "/cc/slots") && !strcmp(sig, "b"))
114 | {
115 | usedCCs_.clear();
116 | ConstBitSpan bits(args[0].b->data, 8 * args[0].b->size);
117 | for (unsigned cc = 0; cc < 512 && cc < bits.bit_size(); ++cc)
118 | {
119 | if (bits.test(cc))
120 | {
121 | usedCCs_.push_back(cc);
122 | std::stringstream path;
123 | path << "/cc" << cc << "/label";
124 | synth.sendMessage(*client, delay, path.str().c_str(), "", nullptr);
125 | path.clear();
126 | path << "/cc" << cc << "/value";
127 | synth.sendMessage(*client, delay, path.str().c_str(), "", nullptr);
128 | path.clear();
129 | path << "/cc" << cc << "/default";
130 | synth.sendMessage(*client, delay, path.str().c_str(), "", nullptr);
131 | }
132 | }
133 | } else if (matchOSC("/cc&/label", path, indices) && !strcmp(sig, "s")) {
134 | lastLabel = args[0].s;
135 | } else if (matchOSC("/cc&/value", path, indices) && !strcmp(sig, "f")) {
136 | lastCCValue = args[0].f;
137 | } else if (matchOSC("/cc&/default", path, indices) && !strcmp(sig, "f")) {
138 | lastCCDefault = args[0].f;
139 | }
140 | }
141 |
142 | private:
143 | sfz::Sfizz synth;
144 | sfz::ClientPtr client = synth.createClient(this);
145 | std::vector usedCCs_;
146 | mutable std::string lastLabel;
147 | mutable float lastCCValue;
148 | mutable float lastCCDefault;
149 |
150 | bool matchOSC(const char *pattern, const char *path, unsigned *indices)
151 | {
152 | unsigned nthIndex = 0;
153 |
154 | while (const char *endp = std::strchr(pattern, '&'))
155 | {
156 | size_t length = endp - pattern;
157 | if (std::strncmp(pattern, path, length))
158 | return false;
159 | pattern += length;
160 | path += length;
161 |
162 | length = 0;
163 | while (std::isdigit(path[length]))
164 | ++length;
165 |
166 | auto result = std::from_chars(path, path + length, indices[nthIndex++]);
167 | if (result.ec != std::errc())
168 | return false;
169 |
170 | pattern += 1;
171 | path += length;
172 | }
173 |
174 | return !std::strcmp(path, pattern);
175 | }
176 | };
177 |
178 | EMSCRIPTEN_BINDINGS(CLASS_Synthesizer)
179 | {
180 | register_vector("vector");
181 | class_("SfizzWrapper")
182 | .constructor()
183 | .function("load", &SfizzWrapper::load)
184 | .function("noteOff", &SfizzWrapper::noteOff)
185 | .function("noteOn", &SfizzWrapper::noteOn)
186 | .function("cc", &SfizzWrapper::cc)
187 | .function("aftertouch", &SfizzWrapper::aftertouch)
188 | .function("pitchWheel", &SfizzWrapper::pitchWheel)
189 | .function("numRegions", &SfizzWrapper::numRegions)
190 | .function("numActiveVoices", &SfizzWrapper::numActiveVoices)
191 | .function("usedCCs", &SfizzWrapper::usedCCs)
192 | .function("ccLabel", &SfizzWrapper::ccLabel)
193 | .function("ccValue", &SfizzWrapper::ccValue)
194 | .function("ccDefault", &SfizzWrapper::ccDefault)
195 | .function("render", &SfizzWrapper::render, allow_raw_pointers());
196 | }
197 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Copyright 2021 Paul Ferrand
2 |
3 | // Permission is hereby granted, free of charge, to any person obtaining a
4 | // copy of this software and associated documentation files (the "Software"),
5 | // to deal in the Software without restriction, including without limitation
6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | // and/or sell copies of the Software, and to permit persons to whom the
8 | // Software is furnished to do so, subject to the following conditions:
9 | //
10 | // The above copyright notice and this permission notice shall be included in
11 | // all copies or substantial portions of the Software.
12 | //
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 | // DEALINGS IN THE SOFTWARE.
20 |
21 | import SfizzNode from './sfizz-node.js';
22 |
23 | class SfizzApp {
24 |
25 | constructor() {
26 | this._kbKeyShortcuts = [
27 | 'z', 's', 'x', 'd', 'c', 'v', 'g', 'b', 'h', 'n', 'j', 'm',
28 | 'q', '2', 'w', '3', 'e', 'r', '5', 't', '6', 'y', '7', 'u', 'i'
29 | ]
30 | this._overlay = null;
31 | this._keyboard = null;
32 | this._keyboardButtons = null;
33 | this._reloadButton = null;
34 | this._context = null;
35 | this._synthNode = null;
36 | this._volumeNode = null;
37 | this._toggleState = false;
38 | this._editor = null;
39 | this._ctrlDown = false;
40 | this._noteStates = new Array(128);
41 | for (let i = 0; i < 128; i++) {
42 | this._noteStates[i] = { midiState: false, mouseState: false, keyboardState: false, element: undefined, whiteKey: undefined };
43 | }
44 | this._kbOctaveShift = 5;
45 | this._numRegions = null;
46 | this._activeVoices = null;
47 | this._leftControlColumn = null;
48 | this._sliders = new Array(512);
49 | for (let i = 0; i < 512; i++) {
50 | this._sliders[i] = { used: false, label: '', default: 0.0, element: undefined, input: undefined };
51 | }
52 | }
53 |
54 | _initializeView() {
55 | this._overlay = document.getElementById('overlay');
56 | this._reloadButton = document.getElementById('reload-text');
57 | this._reloadButton.addEventListener(
58 | 'mouseup', () => this._reloadSfz());
59 | this._keyboard = document.getElementById('keyboard');
60 | this._keyboardButtons = document.getElementById('keyboard-buttons');
61 | this._keyboardButtons = document.getElementById('keyboard-buttons');
62 | this._numRegions = document.getElementById('num-regions');
63 | this._activeVoices = document.getElementById('active-voices');
64 | this._leftControlColumn = document.getElementById('left-control-column');
65 |
66 | $('#octave-down').on('mousedown', () => {
67 | if (this._kbOctaveShift > 0)
68 | this._kbOctaveShift--;
69 |
70 | this._updateKeyboardLabels();
71 | });
72 |
73 | $('#octave-up').on('mousedown', () => {
74 | if (this._kbOctaveShift < 7)
75 | this._kbOctaveShift++;
76 |
77 | this._updateKeyboardLabels();
78 | });
79 | }
80 |
81 | _reloadSfz() {
82 |
83 | this._sliders.forEach((slider) => {
84 | slider.used = false;
85 | slider.element = undefined;
86 | slider.input = undefined;
87 | });
88 |
89 | while (this._leftControlColumn.firstChild)
90 | this._leftControlColumn.removeChild(this._leftControlColumn.firstChild);
91 |
92 | this._post({ type: 'text', sfz: this._editor.getValue() });
93 | }
94 |
95 | _setupKeyCaptures() {
96 | document.addEventListener('keydown', e => {
97 | if (e.ctrlKey && e.key === 's') {
98 | e.preventDefault(); // Prevent the Save dialog to open
99 | this._reloadSfz();
100 | return;
101 | }
102 |
103 | if (this._editor.isFocused())
104 | return;
105 |
106 | this._kbKeyShortcuts.some((shortcut, idx) => {
107 | if (e.key == shortcut) {
108 | const note = this._kbOctaveShift * 12 + idx;
109 | const state = this._noteStates[note];
110 | if (state.keyboardState)
111 | return;
112 |
113 | if (!state.mouseState && !state.midiState)
114 | this._noteOn(note, 0.8);
115 |
116 | this._noteStates[note].keyboardState = true;
117 | this._updateKeyboardState(note);
118 |
119 | return true;
120 | }
121 | });
122 | });
123 |
124 | document.addEventListener('keyup', e => {
125 | if (this._editor.isFocused())
126 | return;
127 |
128 | this._kbKeyShortcuts.some((shortcut, idx) => {
129 | if (e.key == shortcut) {
130 | const note = this._kbOctaveShift * 12 + idx;
131 | const state = this._noteStates[note];
132 |
133 | if (!state.mouseState && !state.midiState)
134 | this._noteOff(note, 0.8);
135 |
136 | this._noteStates[note].keyboardState = false;
137 | this._updateKeyboardState(note);
138 |
139 | return true;
140 | }
141 | });
142 | });
143 | }
144 |
145 | _setupEditor() {
146 | this._editor = ace.edit("editor");
147 | ace.config.set('basePath', 'https://pagecdn.io/lib/ace/1.4.12/')
148 | this._editor.setKeyboardHandler("ace/keyboard/sublime");
149 | this._editor.commands.addCommand({
150 | name: 'reload',
151 | bindKey: { win: 'Ctrl-S', mac: 'Command-S' },
152 | exec: (editor) => this._reloadSfz(),
153 | readOnly: true, // false if this command should not apply in readOnly mode
154 | });
155 | }
156 |
157 | _setupWebMidi() {
158 | if (navigator.requestMIDIAccess) {
159 | navigator.requestMIDIAccess({ name: "midi", sysex: false })
160 | .then((midiAccess) => {
161 | var devicesNames = [];
162 | midiAccess.inputs.forEach((input, idx) => {
163 | input.onmidimessage = this._dispatchMIDIMessage.bind(this);
164 | devicesNames.push(input.name);
165 | })
166 |
167 | if (devicesNames.length > 0) {
168 | document.getElementById("connected-devices").textContent =
169 | 'Connected devices: ' + devicesNames.join(', ');
170 | }
171 | }, () => console.log("MIDI connection failure"));
172 | } else {
173 | console.log('WebMIDI is not supported in this browser.');
174 | }
175 | }
176 |
177 | _noteNumberToLabel(note) {
178 | const octave = Math.floor(note / 12 - 1);
179 | const noteNames = ["c", "cs", "d", "ds", "e", "f", "fs", "g", "gs", "a", "as", "b"];
180 | const noteIdx = (note % 12);
181 | return noteNames[noteIdx] + octave;
182 | }
183 |
184 | _labelToNoteNumber(label) {
185 | const noteNames = ["c", "cs", "d", "ds", "e", "f", "fs", "g", "gs", "a", "as", "b"];
186 | const octave = parseInt(label.length > 3 ? label.slice(-2) : label.slice(-1));
187 | const noteName = label.slice(0, -1);
188 | const noteIdx = noteNames.findIndex((x) => noteName == x);
189 | if (noteIdx < 0 || octave < -1)
190 | return -1;
191 |
192 |
193 | return (octave + 1) * 12 + noteIdx;
194 | }
195 |
196 | _noteOn(note, velocity) {
197 | this._post({ type: 'note_on', number: note, value: velocity });
198 | const id = this._noteNumberToLabel(note);
199 | var key = document.getElementById(id);
200 | if (key)
201 | key.style.backgroundColor = "steelblue";
202 | };
203 |
204 | _noteOff(note) {
205 | this._post({ type: 'note_off', number: note, value: 0.0 });
206 | const id = this._noteNumberToLabel(note);
207 | var key = document.getElementById(id);
208 | if (key)
209 | key.style.backgroundColor = key.classList.contains('white') ? "white" : "black";
210 | };
211 |
212 | _dispatchMIDIMessage(message) {
213 | // console.log('Midi message:', message);
214 | var command = message.data[0];
215 | var number = message.data[1];
216 | var value = (message.data.length > 2) ? message.data[2] / 127.0 : 0; // a velocity value might not be included with a noteOff command
217 |
218 | switch (command) {
219 | case 144: { // noteOn
220 | const state = this._noteStates[number];
221 | if (value > 0) {
222 | if (!state.mouseState && !state.keyboardState)
223 | this._noteOn(number, value);
224 |
225 | this._noteStates[number].midiState = true;
226 | } else {
227 | if (!state.mouseState && !state.keyboardState)
228 | this._noteOff(number, value);
229 |
230 | this._noteStates[number].midiState = false;
231 | }
232 | this._updateKeyboardState(number);
233 | }
234 | break;
235 | case 128: { // noteOff
236 | const state = this._noteStates[number];
237 | if (!state.mouseState && !state.keyboardState)
238 | this._noteOff(number, value);
239 |
240 | this._noteStates[number].midiState = false;
241 | this._updateKeyboardState(number);
242 | }
243 | break;
244 | case 176: // cc
245 | this._post({ type: 'cc', number: number, value: value });
246 | var element = this._sliders[number].element;
247 | if (element != undefined)
248 | element.slider('set value', value, false);
249 |
250 | var input = this._sliders[number].input;
251 | if (input != undefined)
252 | input.val(parseFloat(value.toFixed(4)));
253 |
254 | break;
255 | case 208: // cc
256 | this._post({ type: 'aftertouch', value: value });
257 | break;
258 | case 224: // pitch wheel
259 | var pitch = (((message.data[2] << 7) + message.data[1]) - 8192);
260 | pitch = Math.max(-8191, Math.min(8191, pitch)) / 8191.0;
261 | this._post({ type: 'pitch_wheel', value: pitch });
262 | break;
263 | }
264 | }
265 |
266 | _onMessage(message) {
267 | if (message.data.numRegions != null)
268 | this._numRegions.textContent = message.data.numRegions;
269 |
270 | if (message.data.activeVoices != null)
271 | this._activeVoices.textContent = message.data.activeVoices;
272 |
273 | if (message.data.cc != null) {
274 | var slider = this._sliders[message.data.cc];
275 | slider.used = true;
276 | if (message.data.label != '')
277 | slider.label = message.data.label;
278 | else
279 | slider.label = 'CC ' + message.data.cc;
280 |
281 | slider.default = message.data.default;
282 |
283 |
284 | const sliderId = 'slider-' + message.data.cc;
285 | const inputId = 'slider-input-' + message.data.cc;
286 | var newSlider = document.createElement('div');
287 | newSlider.innerHTML = `${slider.label} `
288 | this._leftControlColumn.appendChild(newSlider);
289 |
290 | var jqSlider = $('#' + sliderId).attr('class', 'ui slider');
291 | var jqInput = $('#' + inputId);
292 | jqSlider.slider({
293 | min: 0, max: 1, step: 0,
294 | onMove: (element, value) => {
295 | this._post({ type: 'cc', number: message.data.cc, value: value });
296 | jqInput.val(parseFloat(value.toFixed(4)));
297 | }
298 | })
299 | .slider('set value', message.data.value)
300 | .on('dblclick', () => jqSlider.slider('set value', message.data.default ));
301 | jqInput.on('change', () => {
302 | const floatVal = parseFloat(jqInput.val());
303 | if (floatVal != NaN && floatVal >= 0 && floatVal <= 1) {
304 | jqSlider.slider('set value', floatVal)
305 | } else {
306 | jqInput.val(jqSlider.slider('get value'));
307 | }
308 | });
309 |
310 | slider.element = jqSlider;
311 | slider.input = jqInput;
312 | }
313 | }
314 |
315 | async _initializeAudio() {
316 | this._context = new AudioContext();
317 | this._context.audioWorklet.addModule('./sfizz-processor.js').then(() => {
318 | this._synthNode = new SfizzNode(this._context);
319 | this._volumeNode = new GainNode(this._context, { gain: 0.25 });
320 | this._synthNode.connect(this._volumeNode)
321 | .connect(this._context.destination);
322 | this._synthNode.port.onmessage = this._onMessage.bind(this);
323 |
324 | this._overlay.style.display = "none";
325 | this._reloadButton.classList.remove('disabled');
326 | this._context.resume();
327 | this._reloadSfz();
328 | });
329 | }
330 |
331 | _post(message) {
332 | this._synthNode.port.postMessage(message);
333 | }
334 |
335 | _updateKeyboardState(note) {
336 | const state = this._noteStates[note];
337 | if (!state.element)
338 | return;
339 |
340 | if (!state.midiState && !state.mouseState && !state.keyboardState) {
341 | if (state.whiteKey == true)
342 | this._noteStates[note].element.style.backgroundColor = 'white';
343 | else if (state.whiteKey == false)
344 | this._noteStates[note].element.style.backgroundColor = 'black';
345 | } else {
346 | this._noteStates[note].element.style.backgroundColor = 'steelblue';
347 | }
348 | }
349 |
350 | _updateKeyboardLabels() {
351 | const start = this._kbOctaveShift * 12;
352 | const stop = this._kbOctaveShift * 12 + this._kbKeyShortcuts.length;
353 | this._noteStates.forEach((e, idx) => {
354 | if (!e.element)
355 | return;
356 |
357 | if (idx < start || idx >= stop) {
358 | e.element.textContent = "";
359 | } else {
360 | e.element.textContent = this._kbKeyShortcuts[idx - start];
361 | }
362 |
363 | if (e.keyboardState && !e.midiState && !e.mouseState)
364 | this._noteOff(idx);
365 |
366 | e.keyboardState = false;
367 | this._updateKeyboardState(idx);
368 | });
369 | }
370 |
371 | _buildKeyboard() {
372 | document.getElementById('keyboard-prototype').hidden = false;
373 | var whitePrototype = document.getElementById('white-prototype');
374 | var blackPrototype = document.getElementById('black-prototype');
375 | const whiteKeyWidth = whitePrototype.offsetWidth;
376 | const blackKeyWidth = blackPrototype.offsetWidth;
377 | const whiteKeyHeight = whitePrototype.offsetHeight;
378 | const blackKeyHeight = blackPrototype.offsetHeight;
379 | document.getElementById('keyboard-prototype').hidden = true;
380 | const keyNames = ['c', 'd', 'e', 'f', 'g', 'a', 'b'];
381 | const octaveWidth = keyNames.length * whiteKeyWidth;
382 | const blackOffset = whiteKeyWidth - blackKeyWidth / 2;
383 | const blackPositions = [0, 1, 3, 4, 5];
384 | var whiteCount = 0;
385 |
386 | var makeKey = (pos, id, cls) => {
387 | var key = document.createElement('button');
388 | key.className = cls;
389 | key.style = 'left: ' + pos + 'px;';
390 | key.id = id;
391 | const note = this._labelToNoteNumber(id);
392 | this._noteStates[note].element = key;
393 |
394 | if (cls == 'white') {
395 | whiteCount += 1;
396 | this._noteStates[note].whiteKey = true;
397 | } else {
398 | this._noteStates[note].whiteKey = false;
399 | }
400 |
401 | if (note >= 0) {
402 | const keyHeight = (cls == 'white') ? whiteKeyHeight : blackKeyHeight;
403 | key.addEventListener('mousedown', (e) => {
404 | const state = this._noteStates[note];
405 | if (!state.midiState
406 | && !state.keyboardState)
407 | this._noteOn(note, e.offsetY / keyHeight);
408 |
409 | this._noteStates[note].mouseState = true;
410 | this._updateKeyboardState(note);
411 | });
412 | key.addEventListener('mouseup', () => {
413 | const state = this._noteStates[note];
414 | if (!state.midiState && !state.keyboardState)
415 | this._noteOff(note);
416 |
417 | this._noteStates[note].mouseState = false;
418 | this._updateKeyboardState(note);
419 | });
420 | key.addEventListener('mouseleave', () => {
421 | const state = this._noteStates[note];
422 | if (state.mouseState && !state.midiState && !state.keyboardState)
423 | this._noteOff(note);
424 |
425 | this._noteStates[note].mouseState = false;
426 | this._updateKeyboardState(note);
427 | });
428 | key.addEventListener('mouseenter', (e) => {
429 | const state = this._noteStates[note];
430 | if ((e.buttons & 1)) {
431 | if (!state.midiState && !state.keyboardState)
432 | this._noteOn(note, e.offsetY / keyHeight);
433 |
434 | this._noteStates[note].mouseState = true;
435 | }
436 | this._updateKeyboardState(note);
437 | });
438 | }
439 | this._keyboard.appendChild(key);
440 | };
441 |
442 | var makeOctave = (shift, octaveNumber) => {
443 | keyNames.forEach((keyName, idx) =>
444 | makeKey(shift + idx * whiteKeyWidth, keyName + octaveNumber, 'white'));
445 | blackPositions.forEach((whiteIdx) =>
446 | makeKey(shift + whiteIdx * whiteKeyWidth + blackOffset, keyNames[whiteIdx] + 's' + octaveNumber, 'black'));
447 | };
448 |
449 | makeKey(0, 'a0', 'white');
450 | makeKey(whiteKeyWidth, 'b0', 'white');
451 | makeKey(blackOffset, 'as0', 'black');
452 | for (let i = 1; i < 8; i++)
453 | makeOctave((i - 1) * octaveWidth + 2 * whiteKeyWidth, i);
454 | makeKey(7 * octaveWidth + 2 * whiteKeyWidth, 'c8', 'white');
455 | this._keyboard.lastChild.style.borderRight = 'solid 1px';
456 | const keyBoardWidth = whiteCount * whiteKeyWidth;
457 | this._keyboard.style.width = keyBoardWidth.toString() + 'px'; // resize the keyboard to center it
458 | this._keyboardButtons.style.width = keyBoardWidth.toString() + 'px';
459 | this._updateKeyboardLabels();
460 | }
461 |
462 | onWindowLoad() {
463 | this._initializeView();
464 | this._buildKeyboard();
465 | this._setupEditor();
466 | document.body.addEventListener('click', () => {
467 | this._initializeAudio();
468 | this._setupKeyCaptures();
469 | this._setupWebMidi();
470 | }, { once: true });
471 | }
472 | }
473 |
474 | const sfizzApp = new SfizzApp();
475 | window.addEventListener('load', () => sfizzApp.onWindowLoad());
476 |
--------------------------------------------------------------------------------
/ace/mode-sfz.js:
--------------------------------------------------------------------------------
1 | define("ace/mode/sfz_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module){/* ***** BEGIN LICENSE BLOCK *****
2 | * Distributed under the BSD license:
3 | *
4 | * Copyright (c) 2012, Ajax.org B.V.
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the following conditions are met:
9 | * * Redistributions of source code must retain the above copyright
10 | * notice, this list of conditions and the following disclaimer.
11 | * * Redistributions in binary form must reproduce the above copyright
12 | * notice, this list of conditions and the following disclaimer in the
13 | * documentation and/or other materials provided with the distribution.
14 | * * Neither the name of Ajax.org B.V. nor the
15 | * names of its contributors may be used to endorse or promote products
16 | * derived from this software without specific prior written permission.
17 | *
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
22 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | *
29 | * ***** END LICENSE BLOCK ***** */
30 | "use strict";
31 | var oop = require("../lib/oop");
32 | var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
33 | var SFZHighlightRules = function () {
34 | this.$rules = {
35 | start: [{
36 | include: "#comment"
37 | }, {
38 | include: "#headers"
39 | }, {
40 | include: "#sfz1_sound-source"
41 | }, {
42 | include: "#sfz1_instrument-settings"
43 | }, {
44 | include: "#sfz1_region-logic"
45 | }, {
46 | include: "#sfz1_performance-parameters"
47 | }, {
48 | include: "#sfz1_modulation"
49 | }, {
50 | include: "#sfz1_effects"
51 | }, {
52 | include: "#sfz2_directives"
53 | }, {
54 | include: "#sfz2_sound-source"
55 | }, {
56 | include: "#sfz2_instrument-settings"
57 | }, {
58 | include: "#sfz2_region-logic"
59 | }, {
60 | include: "#sfz2_performance-parameters"
61 | }, {
62 | include: "#sfz2_modulation"
63 | }, {
64 | include: "#sfz2_curves"
65 | }, {
66 | include: "#aria_instrument-settings"
67 | }, {
68 | include: "#aria_region-logic"
69 | }, {
70 | include: "#aria_performance-parameters"
71 | }, {
72 | include: "#aria_modulation"
73 | }, {
74 | include: "#aria_curves"
75 | }, {
76 | include: "#aria_effects"
77 | }],
78 | "#comment": [{
79 | token: "punctuation.definition.comment.sfz",
80 | regex: /\/\*/,
81 | push: [{
82 | token: "punctuation.definition.comment.sfz",
83 | regex: /\*\//,
84 | next: "pop"
85 | }, {
86 | defaultToken: "comment.block.sfz"
87 | }]
88 | }, {
89 | token: [
90 | "punctuation.whitespace.comment.leading.sfz",
91 | "punctuation.definition.comment.sfz"
92 | ],
93 | regex: /((?:[\s]+)?)(\/\/)(?:\s*(?=\s|$))?/,
94 | push: [{
95 | token: "comment.line.double-slash.sfz",
96 | regex: /(?=$)/,
97 | next: "pop"
98 | }, {
99 | defaultToken: "comment.line.double-slash.sfz"
100 | }]
101 | }],
102 | "#headers": [{
103 | token: [
104 | "punctuation.definition.tag.begin.sfz",
105 | "keyword.control.$2.sfz",
106 | "punctuation.definition.tag.begin.sfz"
107 | ],
108 | regex: /(<)(control|global|master|group|region|curve|effect|midi)(>)/,
109 | comment: "Headers"
110 | }, {
111 | token: "invalid.sfz",
112 | regex: /<.*(?!(?:control|global|master|group|region|curve|effect|midi))>/,
113 | comment: "Non-compliant headers"
114 | }],
115 | "#sfz1_sound-source": [{
116 | token: [
117 | "variable.language.sound-source.$1.sfz",
118 | "keyword.operator.assignment.sfz"
119 | ],
120 | regex: /\b(sample)(=?)/,
121 | push: [{
122 | token: "meta.opcode.sfz",
123 | regex: /(?=(?:\s\/\/|$))/,
124 | next: "pop"
125 | }, {
126 | defaultToken: "meta.opcode.sfz"
127 | }],
128 | comment: "opcodes: (sample): (any string)"
129 | }, {
130 | token: "variable.language.sound-source.$1.sfz",
131 | regex: /\bdelay(?:_random|_oncc\d{1,3})?\b/,
132 | push: [{
133 | token: "meta.opcode.sfz",
134 | regex: /\s|$/,
135 | next: "pop"
136 | }, {
137 | include: "#float_0-100"
138 | }, {
139 | defaultToken: "meta.opcode.sfz"
140 | }],
141 | comment: "opcodes: (delay|delay_random|delay_onccN): (0 to 100 percent)"
142 | }, {
143 | token: "variable.language.sound-source.$1.sfz",
144 | regex: /\boffset(?:_random|_oncc\d{1,3})?\b/,
145 | push: [{
146 | token: "meta.opcode.sfz",
147 | regex: /\s|$/,
148 | next: "pop"
149 | }, {
150 | include: "#int_positive"
151 | }, {
152 | defaultToken: "meta.opcode.sfz"
153 | }],
154 | comment: "opcodes: (offset|offset_random|offset_onccN): (0 to 4294967296 sample units)"
155 | }, {
156 | token: "variable.language.sound-source.$1.sfz",
157 | regex: /\bend\b/,
158 | push: [{
159 | token: "meta.opcode.sfz",
160 | regex: /\s|$/,
161 | next: "pop"
162 | }, {
163 | include: "#int_positive_or_neg1"
164 | }, {
165 | defaultToken: "meta.opcode.sfz"
166 | }],
167 | comment: "opcodes: (end): (-1 to 4294967296 sample units)"
168 | }, {
169 | token: "variable.language.sound-source.$1.sfz",
170 | regex: /\bcount\b/,
171 | push: [{
172 | token: "meta.opcode.sfz",
173 | regex: /\s|$/,
174 | next: "pop"
175 | }, {
176 | include: "#int_positive"
177 | }, {
178 | defaultToken: "meta.opcode.sfz"
179 | }],
180 | comment: "opcodes: (count): (0 to 4294967296 loops)"
181 | }, {
182 | token: "variable.language.sound-source.$1.sfz",
183 | regex: /\bloop_mode\b/,
184 | push: [{
185 | token: "meta.opcode.sfz",
186 | regex: /\s|$/,
187 | next: "pop"
188 | }, {
189 | include: "#string_loop_mode"
190 | }, {
191 | defaultToken: "meta.opcode.sfz"
192 | }],
193 | comment: "opcodes: (loop_mode): (no_loop|one_shot|loop_continuous|loop_sustain)"
194 | }, {
195 | token: "variable.language.sound-source.$1.sfz",
196 | regex: /\b(?:loop_start|loop_end)\b/,
197 | push: [{
198 | token: "meta.opcode.sfz",
199 | regex: /\s|$/,
200 | next: "pop"
201 | }, {
202 | include: "#int_positive"
203 | }, {
204 | defaultToken: "meta.opcode.sfz"
205 | }],
206 | comment: "opcodes: (loop_start|loop_end): (0 to 4294967296 sample units)"
207 | }, {
208 | token: "variable.language.sound-source.$1.sfz",
209 | regex: /\b(?:sync_beats|sync_offset)\b/,
210 | push: [{
211 | token: "meta.opcode.sfz",
212 | regex: /\s|$/,
213 | next: "pop"
214 | }, {
215 | include: "#float_0-32"
216 | }, {
217 | defaultToken: "meta.opcode.sfz"
218 | }],
219 | comment: "opcodes: (sync_beats|sync_offset): (0 to 32 beats)"
220 | }],
221 | "#sfz1_instrument-settings": [{
222 | token: "variable.language.instrument-settings.$1.sfz",
223 | regex: /\b(?:group|polyphony_group|off_by)\b/,
224 | push: [{
225 | token: "meta.opcode.sfz",
226 | regex: /\s|$/,
227 | next: "pop"
228 | }, {
229 | include: "#int_positive"
230 | }, {
231 | defaultToken: "meta.opcode.sfz"
232 | }],
233 | comment: "opcodes: (group|polyphony_group|off_by): (0 to 4294967296 sample units)"
234 | }, {
235 | token: "variable.language.instrument-settings.$1.sfz",
236 | regex: /\boff_mode\b/,
237 | push: [{
238 | token: "meta.opcode.sfz",
239 | regex: /\s|$/,
240 | next: "pop"
241 | }, {
242 | include: "#string_fast-normal-time"
243 | }, {
244 | defaultToken: "meta.opcode.sfz"
245 | }],
246 | comment: "opcodes: (off_mode): (fast|normal)"
247 | }, {
248 | token: "variable.language.instrument-settings.$1.sfz",
249 | regex: /\boutput\b/,
250 | push: [{
251 | token: "meta.opcode.sfz",
252 | regex: /\s|$/,
253 | next: "pop"
254 | }, {
255 | include: "#int_0-1024"
256 | }, {
257 | defaultToken: "meta.opcode.sfz"
258 | }],
259 | comment: "opcodes: (output): (0 to 1024 MIDI Nodes)"
260 | }],
261 | "#sfz1_region-logic": [{
262 | token: "variable.language.region-logic.key-mapping.$1.sfz",
263 | regex: /\b(?:key|lokey|hikey)\b/,
264 | push: [{
265 | token: "meta.opcode.sfz",
266 | regex: /\s|$/,
267 | next: "pop"
268 | }, {
269 | include: "#int_0-127_or_string_note"
270 | }, {
271 | defaultToken: "meta.opcode.sfz"
272 | }],
273 | comment: "opcodes: (key|lokey|hikey): (0 to 127 MIDI Note or C-1 to G#9 Note)"
274 | }, {
275 | token: "variable.language.region-logic.key-mapping.$1.sfz",
276 | regex: /\b(?:lovel|hivel)\b/,
277 | push: [{
278 | token: "meta.opcode.sfz",
279 | regex: /\s|$/,
280 | next: "pop"
281 | }, {
282 | include: "#int_0-127"
283 | }, {
284 | defaultToken: "meta.opcode.sfz"
285 | }],
286 | comment: "opcodes: (love|hivel): (0 to 127 MIDI Velocity)"
287 | }, {
288 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
289 | regex: /\b(?:lochan|hichan)\b/,
290 | push: [{
291 | token: "meta.opcode.sfz",
292 | regex: /\s|$/,
293 | next: "pop"
294 | }, {
295 | include: "#int_1-16"
296 | }, {
297 | defaultToken: "meta.opcode.sfz"
298 | }],
299 | comment: "opcodes: (lochan|hichan): (1 to 16 MIDI Channel)"
300 | }, {
301 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
302 | regex: /\b(?:lo|hi)cc(?:\d{1,3})?\b/,
303 | push: [{
304 | token: "meta.opcode.sfz",
305 | regex: /\s|$/,
306 | next: "pop"
307 | }, {
308 | include: "#int_0-127"
309 | }, {
310 | defaultToken: "meta.opcode.sfz"
311 | }],
312 | comment: "opcodes: (loccN|hiccN): (0 to 127 MIDI Controller)"
313 | }, {
314 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
315 | regex: /\b(?:lobend|hibend)\b/,
316 | push: [{
317 | token: "meta.opcode.sfz",
318 | regex: /\s|$/,
319 | next: "pop"
320 | }, {
321 | include: "#int_neg8192-8192"
322 | }, {
323 | defaultToken: "meta.opcode.sfz"
324 | }],
325 | comment: "opcodes: (lobend|hibend): (-8192 to 8192 cents)"
326 | }, {
327 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
328 | regex: /\bsw_(?:lokey|hikey|last|down|up|previous)\b/,
329 | push: [{
330 | token: "meta.opcode.sfz",
331 | regex: /\s|$/,
332 | next: "pop"
333 | }, {
334 | include: "#int_0-127_or_string_note"
335 | }, {
336 | defaultToken: "meta.opcode.sfz"
337 | }],
338 | comment: "opcodes: (sw_lokey|sw_hikey|sw_last|sw_down|sw_up|sw_previous): (0 to 127 MIDI Note or C-1 to G#9 Note)"
339 | }, {
340 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
341 | regex: /\bsw_vel\b/,
342 | push: [{
343 | token: "meta.opcode.sfz",
344 | regex: /\s|$/,
345 | next: "pop"
346 | }, {
347 | include: "#string_current-previous"
348 | }, {
349 | defaultToken: "meta.opcode.sfz"
350 | }],
351 | comment: "opcodes: (sw_vel): (current|previous)"
352 | }, {
353 | token: "variable.language.region-logic.internal-conditions.$1.sfz",
354 | regex: /\b(?:lobpm|hibpm)\b/,
355 | push: [{
356 | token: "meta.opcode.sfz",
357 | regex: /\s|$/,
358 | next: "pop"
359 | }, {
360 | include: "#float_0-500"
361 | }, {
362 | defaultToken: "meta.opcode.sfz"
363 | }],
364 | comment: "opcodes: (lobpm|hibpm): (0 to 500 BPM)"
365 | }, {
366 | token: "variable.language.region-logic.internal-conditions.$1.sfz",
367 | regex: /\b(?:lochanaft|hichanaft|lopolyaft|hipolyaft)\b/,
368 | push: [{
369 | token: "meta.opcode.sfz",
370 | regex: /\s|$/,
371 | next: "pop"
372 | }, {
373 | include: "#int_0-127"
374 | }, {
375 | defaultToken: "meta.opcode.sfz"
376 | }],
377 | comment: "opcodes: (lochanaft|hichanaft|lopolyaft|hipolyaft): (0 to 127 MIDI Controller)"
378 | }, {
379 | token: "variable.language.region-logic.internal-conditions.$1.sfz",
380 | regex: /\b(?:lorand|hirand)\b/,
381 | push: [{
382 | token: "meta.opcode.sfz",
383 | regex: /\s|$/,
384 | next: "pop"
385 | }, {
386 | include: "#float_0-1"
387 | }, {
388 | defaultToken: "meta.opcode.sfz"
389 | }],
390 | comment: "opcodes: (lorand|hirand): (0 to 1 float)"
391 | }, {
392 | token: "variable.language.region-logic.internal-conditions.$1.sfz",
393 | regex: /\b(?:seq_length|seq_position)\b/,
394 | push: [{
395 | token: "meta.opcode.sfz",
396 | regex: /\s|$/,
397 | next: "pop"
398 | }, {
399 | include: "#int_1-100"
400 | }, {
401 | defaultToken: "meta.opcode.sfz"
402 | }],
403 | comment: "opcodes: (seq_length|seq_position): (1 to 100 beats)"
404 | }, {
405 | token: "variable.language.region-logic.triggers.$1.sfz",
406 | regex: /\btrigger\b/,
407 | push: [{
408 | token: "meta.opcode.sfz",
409 | regex: /\s|$/,
410 | next: "pop"
411 | }, {
412 | include: "#string_attack-release-first-legato"
413 | }, {
414 | defaultToken: "meta.opcode.sfz"
415 | }],
416 | comment: "opcodes: (trigger): (attack|release|first|legato)"
417 | }, {
418 | token: "variable.language.region-logic.triggers.$1.sfz",
419 | regex: /\bon_(?:lo|hi)cc(?:\d{1,3})?\b/,
420 | push: [{
421 | token: "meta.opcode.sfz",
422 | regex: /\s|$/,
423 | next: "pop"
424 | }, {
425 | include: "#int_neg1-127"
426 | }, {
427 | defaultToken: "meta.opcode.sfz"
428 | }],
429 | comment: "opcodes: (on_loccN|on_hiccN): (-1 to 127 MIDI Controller)"
430 | }],
431 | "#sfz1_performance-parameters": [{
432 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
433 | regex: /\b(?:pan|position|width|amp_veltrack)\b/,
434 | push: [{
435 | token: "meta.opcode.sfz",
436 | regex: /\s|$/,
437 | next: "pop"
438 | }, {
439 | include: "#float_neg100-100"
440 | }, {
441 | defaultToken: "meta.opcode.sfz"
442 | }],
443 | comment: "opcodes: (pan|position|width|amp_veltrack): (-100 to 100 percent)"
444 | }, {
445 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
446 | regex: /\bvolume\b/,
447 | push: [{
448 | token: "meta.opcode.sfz",
449 | regex: /\s|$/,
450 | next: "pop"
451 | }, {
452 | include: "#float_neg144-6"
453 | }, {
454 | defaultToken: "meta.opcode.sfz"
455 | }],
456 | comment: "opcodes: (volume): (-144 to 6 dB)"
457 | }, {
458 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
459 | regex: /\bamp_keycenter\b/,
460 | push: [{
461 | token: "meta.opcode.sfz",
462 | regex: /\s|$/,
463 | next: "pop"
464 | }, {
465 | include: "#int_0-127_or_string_note"
466 | }, {
467 | defaultToken: "meta.opcode.sfz"
468 | }],
469 | comment: "opcodes: (amp_keycenter): (0 to 127 MIDI Note or C-1 to G#9 Note)"
470 | }, {
471 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
472 | regex: /\bamp_keytrack\b/,
473 | push: [{
474 | token: "meta.opcode.sfz",
475 | regex: /\s|$/,
476 | next: "pop"
477 | }, {
478 | include: "#float_neg96-12"
479 | }, {
480 | defaultToken: "meta.opcode.sfz"
481 | }],
482 | comment: "opcodes: (amp_keytrack): (-96 to 12 dB)"
483 | }, {
484 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
485 | regex: /\bamp_velcurve_(?:\d{1,3})?\b/,
486 | push: [{
487 | token: "meta.opcode.sfz",
488 | regex: /\s|$/,
489 | next: "pop"
490 | }, {
491 | include: "#float_0-1"
492 | }, {
493 | defaultToken: "meta.opcode.sfz"
494 | }],
495 | comment: "opcodes: (amp_velcurve_N): (0 to 1 curve)"
496 | }, {
497 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
498 | regex: /\bamp_random\b/,
499 | push: [{
500 | token: "meta.opcode.sfz",
501 | regex: /\s|$/,
502 | next: "pop"
503 | }, {
504 | include: "#float_0-24"
505 | }, {
506 | defaultToken: "meta.opcode.sfz"
507 | }],
508 | comment: "opcodes: (amp_random): (0 to 24 dB)"
509 | }, {
510 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
511 | regex: /\bgain_oncc(?:\d{1,3})?\b/,
512 | push: [{
513 | token: "meta.opcode.sfz",
514 | regex: /\s|$/,
515 | next: "pop"
516 | }, {
517 | include: "#float_neg144-48"
518 | }, {
519 | defaultToken: "meta.opcode.sfz"
520 | }],
521 | comment: "opcodes: (gain_onccN): (-144 to 48 dB)"
522 | }, {
523 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
524 | regex: /\brt_decay\b/,
525 | push: [{
526 | token: "meta.opcode.sfz",
527 | regex: /\s|$/,
528 | next: "pop"
529 | }, {
530 | include: "#float_0-200"
531 | }, {
532 | defaultToken: "meta.opcode.sfz"
533 | }],
534 | comment: "opcodes: (rt_decay): (0 to 200 dB)"
535 | }, {
536 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
537 | regex: /\b(?:xf_cccurve|xf_keycurve|xf_velcurve)\b/,
538 | push: [{
539 | token: "meta.opcode.sfz",
540 | regex: /\s|$/,
541 | next: "pop"
542 | }, {
543 | include: "#string_gain-power"
544 | }, {
545 | defaultToken: "meta.opcode.sfz"
546 | }],
547 | comment: "opcodes: (xf_cccurve|xf_keycurve|xf_velcurve): (gain|power)"
548 | }, {
549 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
550 | regex: /\b(?:xfin_locc(?:\d{1,3})?|xfin_hicc(?:\d{1,3})?|xfout_locc(?:\d{1,3})?|xfout_hicc(?:\d{1,3})?|xfin_lokey|xfin_hikey|xfout_lokey|xfout_hikey|xfin_lovel|xfin_hivel|xfout_lovel|xfout_hivel)\b/,
551 | push: [{
552 | token: "meta.opcode.sfz",
553 | regex: /\s|$/,
554 | next: "pop"
555 | }, {
556 | include: "#int_0-127"
557 | }, {
558 | defaultToken: "meta.opcode.sfz"
559 | }],
560 | comment: "opcodes: (xfin_loccN|xfin_hiccN|xfout_loccN|xfout_hiccN|xfin_lokey|xfin_hikey|xfout_lokey|xfout_hikey|xfin_lovel|xfin_hivel|xfout_lovel|xfout_hivel): (0 to 127 MIDI Velocity)"
561 | }, {
562 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
563 | regex: /\b(?:xfin_lokey|xfin_hikey|xfout_lokey|xfout_hikey)\b/,
564 | push: [{
565 | token: "meta.opcode.sfz",
566 | regex: /\s|$/,
567 | next: "pop"
568 | }, {
569 | include: "#int_0-127_or_string_note"
570 | }, {
571 | defaultToken: "meta.opcode.sfz"
572 | }],
573 | comment: "opcodes: (xfin_lokey|xfin_hikey|xfout_lokey|xfout_hikey): (0 to 127 MIDI Note or C-1 to G#9 Note)"
574 | }, {
575 | token: "variable.language.performance-parameters.pitch.$1.sfz",
576 | regex: /\b(?:bend_up|bend_down|pitch_veltrack)\b/,
577 | push: [{
578 | token: "meta.opcode.sfz",
579 | regex: /\s|$/,
580 | next: "pop"
581 | }, {
582 | include: "#int_neg9600-9600"
583 | }, {
584 | defaultToken: "meta.opcode.sfz"
585 | }],
586 | comment: "opcodes: (bend_up|bend_down|pitch_veltrack): (-9600 to 9600 cents)"
587 | }, {
588 | token: "variable.language.performance-parameters.pitch.$1.sfz",
589 | regex: /\bbend_step\b/,
590 | push: [{
591 | token: "meta.opcode.sfz",
592 | regex: /\s|$/,
593 | next: "pop"
594 | }, {
595 | include: "#int_1-1200"
596 | }, {
597 | defaultToken: "meta.opcode.sfz"
598 | }],
599 | comment: "opcodes: (bend_step): (1 to 1200 cents)"
600 | }, {
601 | token: "variable.language.performance-parameters.pitch.$1.sfz",
602 | regex: /\bpitch_keycenter\b/,
603 | push: [{
604 | token: "meta.opcode.sfz",
605 | regex: /\s|$/,
606 | next: "pop"
607 | }, {
608 | include: "#int_0-127_or_string_note"
609 | }, {
610 | defaultToken: "meta.opcode.sfz"
611 | }],
612 | comment: "opcodes: (pitch_keycenter): (0 to 127 MIDI Note)"
613 | }, {
614 | token: "variable.language.performance-parameters.pitch.$1.sfz",
615 | regex: /\bpitch_keytrack\b/,
616 | push: [{
617 | token: "meta.opcode.sfz",
618 | regex: /\s|$/,
619 | next: "pop"
620 | }, {
621 | include: "#int_neg1200-1200"
622 | }, {
623 | defaultToken: "meta.opcode.sfz"
624 | }],
625 | comment: "opcodes: (pitch_keytrack): (-1200 to 1200 cents)"
626 | }, {
627 | token: "variable.language.performance-parameters.pitch.$1.sfz",
628 | regex: /\bpitch_random\b/,
629 | push: [{
630 | token: "meta.opcode.sfz",
631 | regex: /\s|$/,
632 | next: "pop"
633 | }, {
634 | include: "#int_0-9600"
635 | }, {
636 | defaultToken: "meta.opcode.sfz"
637 | }],
638 | comment: "opcodes: (pitch_random): (0 to 9600 cents)"
639 | }, {
640 | token: "variable.language.performance-parameters.pitch.$1.sfz",
641 | regex: /\btranspose\b/,
642 | push: [{
643 | token: "meta.opcode.sfz",
644 | regex: /\s|$/,
645 | next: "pop"
646 | }, {
647 | include: "#int_neg127-127"
648 | }, {
649 | defaultToken: "meta.opcode.sfz"
650 | }],
651 | comment: "opcodes: (transpose): (-127 to 127 MIDI Note)"
652 | }, {
653 | token: "variable.language.performance-parameters.pitch.$1.sfz",
654 | regex: /\btune\b/,
655 | push: [{
656 | token: "meta.opcode.sfz",
657 | regex: /\s|$/,
658 | next: "pop"
659 | }, {
660 | include: "#int_neg9600-9600"
661 | }, {
662 | defaultToken: "meta.opcode.sfz"
663 | }],
664 | comment: "opcodes: (tune): (-2400 to 2400 cents)"
665 | }, {
666 | token: "variable.language.performance-parameters.filters.$1.sfz",
667 | regex: /\bcutoff\b/,
668 | push: [{
669 | token: "meta.opcode.sfz",
670 | regex: /\s|$/,
671 | next: "pop"
672 | }, {
673 | include: "#float_positive"
674 | }, {
675 | defaultToken: "meta.opcode.sfz"
676 | }],
677 | comment: "opcodes: (cutoff): (0 to arbitrary Hz)"
678 | }, {
679 | token: "variable.language.performance-parameters.filters.$1.sfz",
680 | regex: /\b(?:cutoff_oncc(?:\d{1,3})?|cutoff_chanaft|cutoff_polyaft)\b/,
681 | push: [{
682 | token: "meta.opcode.sfz",
683 | regex: /\s|$/,
684 | next: "pop"
685 | }, {
686 | include: "#int_neg9600-9600"
687 | }, {
688 | defaultToken: "meta.opcode.sfz"
689 | }],
690 | comment: "opcodes: (cutoff_onccN|cutoff_chanaft|cutoff_polyaft): (-9600 to 9600 cents)"
691 | }, {
692 | token: "variable.language.performance-parameters.filters.$1.sfz",
693 | regex: /\bfil_keytrack\b/,
694 | push: [{
695 | token: "meta.opcode.sfz",
696 | regex: /\s|$/,
697 | next: "pop"
698 | }, {
699 | include: "#int_0-1200"
700 | }, {
701 | defaultToken: "meta.opcode.sfz"
702 | }],
703 | comment: "opcodes: (fil_keytrack): (0 to 1200 cents)"
704 | }, {
705 | token: "variable.language.performance-parameters.filters.$1.sfz",
706 | regex: /\bfil_keycenter\b/,
707 | push: [{
708 | token: "meta.opcode.sfz",
709 | regex: /\s|$/,
710 | next: "pop"
711 | }, {
712 | include: "#int_0-127_or_string_note"
713 | }, {
714 | defaultToken: "meta.opcode.sfz"
715 | }],
716 | comment: "opcodes: (fil_keycenter): (0 to 127 MIDI Note)"
717 | }, {
718 | token: "variable.language.performance-parameters.filters.$1.sfz",
719 | regex: /\bfil_random\b/,
720 | push: [{
721 | token: "meta.opcode.sfz",
722 | regex: /\s|$/,
723 | next: "pop"
724 | }, {
725 | include: "#int_0-9600"
726 | }, {
727 | defaultToken: "meta.opcode.sfz"
728 | }],
729 | comment: "opcodes: (fil_random): (0 to 9600 cents)"
730 | }, {
731 | token: "variable.language.performance-parameters.filters.$1.sfz",
732 | regex: /\bfil_type\b/,
733 | push: [{
734 | token: "meta.opcode.sfz",
735 | regex: /\s|$/,
736 | next: "pop"
737 | }, {
738 | include: "#string_lpf-hpf-bpf-brf"
739 | }, {
740 | defaultToken: "meta.opcode.sfz"
741 | }],
742 | comment: "opcodes: (fil_type): (lpf_1p|hpf_1p|lpf_2p|hpf_2p|bpf_2p|brf_2p)"
743 | }, {
744 | token: "variable.language.performance-parameters.filters.$1.sfz",
745 | regex: /\bfil_veltrack\b/,
746 | push: [{
747 | token: "meta.opcode.sfz",
748 | regex: /\s|$/,
749 | next: "pop"
750 | }, {
751 | include: "#int_neg9600-9600"
752 | }, {
753 | defaultToken: "meta.opcode.sfz"
754 | }],
755 | comment: "opcodes: (fil_veltrack): (-9600 to 9600 cents)"
756 | }, {
757 | token: "variable.language.performance-parameters.filters.$1.sfz",
758 | regex: /\bresonance\b/,
759 | push: [{
760 | token: "meta.opcode.sfz",
761 | regex: /\s|$/,
762 | next: "pop"
763 | }, {
764 | include: "#float_0-40"
765 | }, {
766 | defaultToken: "meta.opcode.sfz"
767 | }],
768 | comment: "opcodes: (resonance): (0 to 40 dB)"
769 | }, {
770 | token: "variable.language.performance-parameters.eq.$1.sfz",
771 | regex: /\b(?:eq1_freq|eq2_freq|eq3_freq)\b/,
772 | push: [{
773 | token: "meta.opcode.sfz",
774 | regex: /\s|$/,
775 | next: "pop"
776 | }, {
777 | include: "#float_0-30000"
778 | }, {
779 | defaultToken: "meta.opcode.sfz"
780 | }],
781 | comment: "opcodes: (eq1_freq|eq2_freq|eq3_freq): (0 to 30000 Hz)"
782 | }, {
783 | token: "variable.language.performance-parameters.eq.$1.sfz",
784 | regex: /\b(?:eq[1-3]_freq_oncc(?:\d{1,3})?|eq1_vel2freq|eq2_vel2freq|eq3_vel2freq)\b/,
785 | push: [{
786 | token: "meta.opcode.sfz",
787 | regex: /\s|$/,
788 | next: "pop"
789 | }, {
790 | include: "#float_neg30000-30000"
791 | }, {
792 | defaultToken: "meta.opcode.sfz"
793 | }],
794 | comment: "opcodes: (eq1_freq_onccN|eq2_freq_onccN|eq3_freq_onccN|eq1_vel2freq|eq2_vel2freq|eq3_vel2freq): (-30000 to 30000 Hz)"
795 | }, {
796 | token: "variable.language.performance-parameters.eq.$1.sfz",
797 | regex: /\b(?:eq1_bw|eq2_bw|eq3_bw)\b/,
798 | push: [{
799 | token: "meta.opcode.sfz",
800 | regex: /\s|$/,
801 | next: "pop"
802 | }, {
803 | include: "#float_0-4"
804 | }, {
805 | defaultToken: "meta.opcode.sfz"
806 | }],
807 | comment: "opcodes: (eq1_bw|eq2_bw|eq3_bw): (0.0001 to 4 octaves)"
808 | }, {
809 | token: "variable.language.performance-parameters.eq.$1.sfz",
810 | regex: /\b(?:eq[1-3]_bw_oncc(?:\d{1,3})?|eq1_vel2bw|eq2_vel2bw|eq3_vel2bw)\b/,
811 | push: [{
812 | token: "meta.opcode.sfz",
813 | regex: /\s|$/,
814 | next: "pop"
815 | }, {
816 | include: "#float_neg4-4"
817 | }, {
818 | defaultToken: "meta.opcode.sfz"
819 | }],
820 | comment: "opcodes: (eq1_bw_onccN|eq2_bw_onccN|eq3_bw_onccN|eq1_vel2bw|eq2_vel2bw|eq3_vel2bw): (-30000 to 30000 Hz)"
821 | }, {
822 | token: "variable.language.performance-parameters.eq.$1.sfz",
823 | regex: /\b(?:eq[1-3]_(?:vel2)?gain|eq[1-3]_gain_oncc(?:\d{1,3})?)\b/,
824 | push: [{
825 | token: "meta.opcode.sfz",
826 | regex: /\s|$/,
827 | next: "pop"
828 | }, {
829 | include: "#float_neg96-24"
830 | }, {
831 | defaultToken: "meta.opcode.sfz"
832 | }],
833 | comment: "opcodes: (eq1_gain|eq2_gain|eq3_gain|eq1_gain_onccN|eq2_gain_onccN|eq3_gain_onccN|eq1_vel2gain|eq2_vel2gain|eq3_vel2gain): (-96 to 24 dB)"
834 | }],
835 | "#sfz1_modulation": [{
836 | token: "variable.language.modulation.envelope-generators.$1.sfz",
837 | regex: /\b(?:ampeg|fileg|pitcheg)_(?:(?:attack|decay|delay|hold|release|start|sustain)(?:_oncc(?:\d{1,3})?)?|vel2(?:attack|decay|delay|hold|release|start|sustain))\b/,
838 | push: [{
839 | token: "meta.opcode.sfz",
840 | regex: /\s|$/,
841 | next: "pop"
842 | }, {
843 | include: "#float_0-100"
844 | }, {
845 | defaultToken: "meta.opcode.sfz"
846 | }],
847 | comment: "opcodes: (ampeg_delay_onccN|ampeg_attack_onccN|ampeg_hold_onccN|ampeg_decay_onccN|ampeg_release_onccN|ampeg_vel2delay|ampeg_vel2attack|ampeg_vel2hold|ampeg_vel2decay|ampeg_vel2release|pitcheg_vel2delay|pitcheg_vel2attack|pitcheg_vel2hold|pitcheg_vel2decay|pitcheg_vel2release|fileg_vel2delay|fileg_vel2attack|fileg_vel2hold|fileg_vel2decay|fileg_vel2release): (0 to 100 seconds)"
848 | }, {
849 | token: "variable.language.modulation.envelope-generators.$1.sfz",
850 | regex: /\b(?:pitcheg_depth|fileg_depth|pitcheg_vel2depth|fileg_vel2depth)\b/,
851 | push: [{
852 | token: "meta.opcode.sfz",
853 | regex: /\s|$/,
854 | next: "pop"
855 | }, {
856 | include: "#int_neg12000-12000"
857 | }, {
858 | defaultToken: "meta.opcode.sfz"
859 | }],
860 | comment: "opcodes: (pitcheg_depth|fileg_depth|pitcheg_vel2depth|fileg_vel2depth): (-12000 to 12000 cents)"
861 | }, {
862 | token: "variable.language.modulation.lfo.$1.sfz",
863 | regex: /\bamplfo_(?:depth(?:cc(?:\d{1,3})?)?|depth(?:chan|poly)aft)\b/,
864 | push: [{
865 | token: "meta.opcode.sfz",
866 | regex: /\s|$/,
867 | next: "pop"
868 | }, {
869 | include: "#float_neg20-20"
870 | }, {
871 | defaultToken: "meta.opcode.sfz"
872 | }],
873 | comment: "opcodes: (amplfo_depth|amplfo_depthccN|amplfo_depthchanaft|amplfo_depthpolyaft): (-20 to 20 dB)"
874 | }, {
875 | token: "variable.language.modulation.lfo.$1.sfz",
876 | regex: /\b(?:fillfo|pitchlfo)_(?:depth(?:(?:_on)?cc(?:\d{1,3})?)?|depth(?:chan|poly)aft)\b/,
877 | push: [{
878 | token: "meta.opcode.sfz",
879 | regex: /\s|$/,
880 | next: "pop"
881 | }, {
882 | include: "#int_neg1200-1200"
883 | }, {
884 | defaultToken: "meta.opcode.sfz"
885 | }],
886 | comment: "opcodes: (pitchlfo_depth|pitchlfo_depthccN|pitchlfo_depthchanaft|pitchlfo_depthpolyaft|fillfo_depth|fillfo_depthccN|fillfo_depthchanaft|fillfo_depthpolyaft): (-1200 to 1200 cents)"
887 | }, {
888 | token: "variable.language.modulation.lfo.$1.sfz",
889 | regex: /\b(?:(?:amplfo|fillfo|pitchlfo)_(?:freq|(?:cc(?:\d{1,3})?)?)|freq(?:chan|poly)aft)\b/,
890 | push: [{
891 | token: "meta.opcode.sfz",
892 | regex: /\s|$/,
893 | next: "pop"
894 | }, {
895 | include: "#float_neg200-200"
896 | }, {
897 | defaultToken: "meta.opcode.sfz"
898 | }],
899 | comment: "opcodes: (amplfo_freqccN|amplfo_freqchanaft|amplfo_freqpolyaft|pitchlfo_freqccN|pitchlfo_freqchanaft|pitchlfo_freqpolyaft|fillfo_freqccN|fillfo_freqchanaft|fillfo_freqpolyaft): (-200 to 200 Hz)"
900 | }, {
901 | token: "variable.language.modulation.lfo.$1.sfz",
902 | regex: /\b(?:amplfo|fillfo|pitchlfo)_(?:delay|fade)\b/,
903 | push: [{
904 | token: "meta.opcode.sfz",
905 | regex: /\s|$/,
906 | next: "pop"
907 | }, {
908 | include: "#float_0-100"
909 | }, {
910 | defaultToken: "meta.opcode.sfz"
911 | }],
912 | comment: "opcodes: (amplfo_delay|amplfo_fade|pitchlfo_delay|pitchlfo_fade|fillfo_delay|fillfo_fade): (0 to 100 seconds)"
913 | }, {
914 | token: "variable.language.modulation.lfo.$1.sfz",
915 | regex: /\b(?:amplfo_freq|pitchlfo_freq|fillfo_freq)\b/,
916 | push: [{
917 | token: "meta.opcode.sfz",
918 | regex: /\s|$/,
919 | next: "pop"
920 | }, {
921 | include: "#float_0-20"
922 | }, {
923 | defaultToken: "meta.opcode.sfz"
924 | }],
925 | comment: "opcodes: (amplfo_freq|pitchlfo_freq|fillfo_freq): (0 to 20 Hz)"
926 | }],
927 | "#sfz1_effects": [{
928 | token: "variable.language.effects.$1.sfz",
929 | regex: /\b(?:effect1|effect2)\b/,
930 | push: [{
931 | token: "meta.opcode.sfz",
932 | regex: /\s|$/,
933 | next: "pop"
934 | }, {
935 | include: "#float_0-100"
936 | }, {
937 | defaultToken: "meta.opcode.sfz"
938 | }],
939 | comment: "opcodes: (effect1|effect2): (0 to 100 percent)"
940 | }],
941 | "#sfz2_directives": [{
942 | token: [
943 | "meta.preprocessor.define.sfz",
944 | "meta.generic.define.sfz",
945 | "punctuation.definition.variable.sfz",
946 | "meta.preprocessor.string.sfz",
947 | "meta.generic.define.sfz",
948 | "meta.preprocessor.string.sfz"
949 | ],
950 | regex: /(\#define)(\s+)(\$)([^\s]+)(\s+)(.+)\b/,
951 | comment: "#define statement"
952 | }, {
953 | token: [
954 | "meta.preprocessor.import.sfz",
955 | "meta.generic.include.sfz",
956 | "punctuation.definition.string.begin.sfz",
957 | "meta.preprocessor.string.sfz",
958 | "meta.preprocessor.string.sfz",
959 | "punctuation.definition.string.end.sfz"
960 | ],
961 | regex: /(\#include)(\s+)(")(.+)(?=\.sfz)(\.sfzh?)(")/,
962 | comment: "#include statement"
963 | }, {
964 | token: "variable.other.constant.sfz",
965 | regex: /\$[^\s\=]+/,
966 | comment: "defined variable"
967 | }],
968 | "#sfz2_sound-source": [{
969 | token: [
970 | "variable.language.sound-source.$1.sfz",
971 | "keyword.operator.assignment.sfz"
972 | ],
973 | regex: /\b(default_path)(=?)/,
974 | push: [{
975 | token: "meta.opcode.sfz",
976 | regex: /(?=(?:\s\/\/|$))/,
977 | next: "pop"
978 | }, {
979 | defaultToken: "meta.opcode.sfz"
980 | }],
981 | comment: "opcodes: (default_path): any string"
982 | }, {
983 | token: "variable.language.sound-source.sample-playback.$1.sfz",
984 | regex: /\bdirection\b/,
985 | push: [{
986 | token: "meta.opcode.sfz",
987 | regex: /\s|$/,
988 | next: "pop"
989 | }, {
990 | include: "#string_forward-reverse"
991 | }, {
992 | defaultToken: "meta.opcode.sfz"
993 | }],
994 | comment: "opcodes: (direction): (forward|reverse)"
995 | }, {
996 | token: "variable.language.sound-source.sample-playback.$1.sfz",
997 | regex: /\bloop_count\b/,
998 | push: [{
999 | token: "meta.opcode.sfz",
1000 | regex: /\s|$/,
1001 | next: "pop"
1002 | }, {
1003 | include: "#int_positive"
1004 | }, {
1005 | defaultToken: "meta.opcode.sfz"
1006 | }],
1007 | comment: "opcodes: (loop_count): (0 to 4294967296 loops)"
1008 | }, {
1009 | token: "variable.language.sound-source.sample-playback.$1.sfz",
1010 | regex: /\bloop_type\b/,
1011 | push: [{
1012 | token: "meta.opcode.sfz",
1013 | regex: /\s|$/,
1014 | next: "pop"
1015 | }, {
1016 | include: "#string_forward-backward-alternate"
1017 | }, {
1018 | defaultToken: "meta.opcode.sfz"
1019 | }],
1020 | comment: "opcodes: (loop_type): (forward|backward|alternate)"
1021 | }, {
1022 | token: "variable.language.sound-source.sample-playback.$1.sfz",
1023 | regex: /\bmd5\b/,
1024 | push: [{
1025 | token: "meta.opcode.sfz",
1026 | regex: /\s|$/,
1027 | next: "pop"
1028 | }, {
1029 | include: "#string_md5"
1030 | }, {
1031 | defaultToken: "meta.opcode.sfz"
1032 | }],
1033 | comment: "opcodes: (md5): (128-bit hex md5 hash)"
1034 | }],
1035 | "#sfz2_instrument-settings": [{
1036 | token: "variable.language.instrument-settings.$1.sfz",
1037 | regex: /\boctave_offset\b/,
1038 | push: [{
1039 | token: "meta.opcode.sfz",
1040 | regex: /\s|$/,
1041 | next: "pop"
1042 | }, {
1043 | include: "#int_neg10-10"
1044 | }, {
1045 | defaultToken: "meta.opcode.sfz"
1046 | }],
1047 | comment: "opcodes: (octave_offset): (-10 to 10 octaves)"
1048 | }, {
1049 | token: [
1050 | "variable.language.instrument-settings.$1.sfz",
1051 | "keyword.operator.assignment.sfz"
1052 | ],
1053 | regex: /\b(region_label|label_cc(?:\d{1,3})?)(=?)/,
1054 | push: [{
1055 | token: "meta.opcode.sfz",
1056 | regex: /(?=(?:\s\/\/|$))/,
1057 | next: "pop"
1058 | }, {
1059 | defaultToken: "meta.opcode.sfz"
1060 | }],
1061 | comment: "opcodes: (region_label|label_ccN): (any string)"
1062 | }, {
1063 | token: "variable.language.instrument-settings.$1.sfz",
1064 | regex: /\bset_cc(?:\d{1,3})?\b/,
1065 | push: [{
1066 | token: "meta.opcode.sfz",
1067 | regex: /\s|$/,
1068 | next: "pop"
1069 | }, {
1070 | include: "#int_0-127"
1071 | }, {
1072 | defaultToken: "meta.opcode.sfz"
1073 | }],
1074 | comment: "opcodes: (set_ccN): (0 to 127 MIDI Controller)"
1075 | }, {
1076 | token: "variable.language.instrument-settings.voice-lifecycle.$1.sfz",
1077 | regex: /\b(?:polyphony|note_polyphony)\b/,
1078 | push: [{
1079 | token: "meta.opcode.sfz",
1080 | regex: /\s|$/,
1081 | next: "pop"
1082 | }, {
1083 | include: "#int_0-127"
1084 | }, {
1085 | defaultToken: "meta.opcode.sfz"
1086 | }],
1087 | comment: "opcodes: (polyphony|note_polyphony): (0 to 127 voices)"
1088 | }, {
1089 | token: "variable.language.instrument-settings.voice-lifecycle.$1.sfz",
1090 | regex: /\b(?:note_selfmask|rt_dead)\b/,
1091 | push: [{
1092 | token: "meta.opcode.sfz",
1093 | regex: /\s|$/,
1094 | next: "pop"
1095 | }, {
1096 | include: "#string_on-off"
1097 | }, {
1098 | defaultToken: "meta.opcode.sfz"
1099 | }],
1100 | comment: "opcodes: (note_selfmask|rt_dead): (on|off)"
1101 | }],
1102 | "#sfz2_region-logic": [{
1103 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
1104 | regex: /\b(?:sustain_sw|sostenuto_sw)\b/,
1105 | push: [{
1106 | token: "meta.opcode.sfz",
1107 | regex: /\s|$/,
1108 | next: "pop"
1109 | }, {
1110 | include: "#string_on-off"
1111 | }, {
1112 | defaultToken: "meta.opcode.sfz"
1113 | }],
1114 | comment: "opcodes: (sustain_sw|sostenuto_sw): (on|off)"
1115 | }, {
1116 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
1117 | regex: /\b(?:loprog|hiprog)\b/,
1118 | push: [{
1119 | token: "meta.opcode.sfz",
1120 | regex: /\s|$/,
1121 | next: "pop"
1122 | }, {
1123 | include: "#int_0-127"
1124 | }, {
1125 | defaultToken: "meta.opcode.sfz"
1126 | }],
1127 | comment: "opcodes: (loprog|hiprog): (0 to 127 MIDI program)"
1128 | }],
1129 | "#sfz2_performance-parameters": [{
1130 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
1131 | regex: /\bvolume_oncc(?:\d{1,3})?\b/,
1132 | push: [{
1133 | token: "meta.opcode.sfz",
1134 | regex: /\s|$/,
1135 | next: "pop"
1136 | }, {
1137 | include: "#float_neg144-6"
1138 | }, {
1139 | defaultToken: "meta.opcode.sfz"
1140 | }],
1141 | comment: "opcodes: (volume_onccN): (-144 to 6 dB)"
1142 | }, {
1143 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
1144 | regex: /\bphase\b/,
1145 | push: [{
1146 | token: "meta.opcode.sfz",
1147 | regex: /\s|$/,
1148 | next: "pop"
1149 | }, {
1150 | include: "#string_normal-invert"
1151 | }, {
1152 | defaultToken: "meta.opcode.sfz"
1153 | }],
1154 | comment: "opcodes: (phase): (normal|invert)"
1155 | }, {
1156 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
1157 | regex: /\bwidth_oncc(?:\d{1,3})?\b/,
1158 | push: [{
1159 | token: "meta.opcode.sfz",
1160 | regex: /\s|$/,
1161 | next: "pop"
1162 | }, {
1163 | include: "#float_neg100-100"
1164 | }, {
1165 | defaultToken: "meta.opcode.sfz"
1166 | }],
1167 | comment: "opcodes: (width_onccN): (-100 to 100 percent)"
1168 | }, {
1169 | token: "variable.language.performance-parameters.pitch.$1.sfz",
1170 | regex: /\bbend_smooth\b/,
1171 | push: [{
1172 | token: "meta.opcode.sfz",
1173 | regex: /\s|$/,
1174 | next: "pop"
1175 | }, {
1176 | include: "#int_0-9600"
1177 | }, {
1178 | defaultToken: "meta.opcode.sfz"
1179 | }],
1180 | comment: "opcodes: (bend_smooth): (0 to 9600 cents)"
1181 | }, {
1182 | token: "variable.language.performance-parameters.pitch.$1.sfz",
1183 | regex: /\b(?:bend_stepup|bend_stepdown)\b/,
1184 | push: [{
1185 | token: "meta.opcode.sfz",
1186 | regex: /\s|$/,
1187 | next: "pop"
1188 | }, {
1189 | include: "#int_1-1200"
1190 | }, {
1191 | defaultToken: "meta.opcode.sfz"
1192 | }],
1193 | comment: "opcodes: (bend_stepup|bend_stepdown): (1 to 1200 cents)"
1194 | }, {
1195 | token: "variable.language.performance-parameters.filters.$1.sfz",
1196 | regex: /\b(?:cutoff2|cutoff2_oncc(?:\d{1,3})?)\b/,
1197 | push: [{
1198 | token: "meta.opcode.sfz",
1199 | regex: /\s|$/,
1200 | next: "pop"
1201 | }, {
1202 | include: "#float_positive"
1203 | }, {
1204 | defaultToken: "meta.opcode.sfz"
1205 | }],
1206 | comment: "opcodes: (cutoff2|cutoff2_onccN): (0 to arbitrary Hz)"
1207 | }, {
1208 | token: "variable.language.performance-parameters.filters.$1.sfz",
1209 | regex: /\b(?:resonance_oncc(?:\d{1,3})?|resonance2|resonance2_oncc(?:\d{1,3})?)\b/,
1210 | push: [{
1211 | token: "meta.opcode.sfz",
1212 | regex: /\s|$/,
1213 | next: "pop"
1214 | }, {
1215 | include: "#float_0-40"
1216 | }, {
1217 | defaultToken: "meta.opcode.sfz"
1218 | }],
1219 | comment: "opcodes: (resonance_onccN|resonance2|resonance2_onccN): (0 to 40 dB)"
1220 | }, {
1221 | token: "variable.language.performance-parameters.filters.$1.sfz",
1222 | regex: /\bfil2_type\b/,
1223 | push: [{
1224 | token: "meta.opcode.sfz",
1225 | regex: /\s|$/,
1226 | next: "pop"
1227 | }, {
1228 | include: "#string_lpf-hpf-bpf-brf"
1229 | }, {
1230 | defaultToken: "meta.opcode.sfz"
1231 | }],
1232 | comment: "opcodes: (fil2_type): (lpf_1p|hpf_1p|lpf_2p|hpf_2p|bpf_2p|brf_2p)"
1233 | }],
1234 | "#sfz2_modulation": [{
1235 | token: "variable.language.modulation.envelope-generators.$1.sfz",
1236 | regex: /\beg\d{2}_(?:curve|loop|points|sustain)\b/,
1237 | push: [{
1238 | token: "meta.opcode.sfz",
1239 | regex: /\s|$/,
1240 | next: "pop"
1241 | }, {
1242 | include: "#int_positive"
1243 | }, {
1244 | defaultToken: "meta.opcode.sfz"
1245 | }],
1246 | comment: "opcodes: (egN_(curve|loop|points|sustain)): (positive int)"
1247 | }, {
1248 | token: "variable.language.modulation.envelope-generators.$1.sfz",
1249 | regex: /\beg\d{2}_level\d*(?:_oncc(?:\d{1,3})?)?\b/,
1250 | push: [{
1251 | token: "meta.opcode.sfz",
1252 | regex: /\s|$/,
1253 | next: "pop"
1254 | }, {
1255 | include: "#float_neg1-1"
1256 | }, {
1257 | defaultToken: "meta.opcode.sfz"
1258 | }],
1259 | comment: "opcodes: (egN_level|egN_level_onccX): (-1 to 1 float)"
1260 | }, {
1261 | token: "variable.language.modulation.envelope-generators.$1.sfz",
1262 | regex: /\beg\d{2}_shape\d+\b/,
1263 | push: [{
1264 | token: "meta.opcode.sfz",
1265 | regex: /\s|$/,
1266 | next: "pop"
1267 | }, {
1268 | include: "#float_neg10-10"
1269 | }, {
1270 | defaultToken: "meta.opcode.sfz"
1271 | }],
1272 | comment: "opcodes: (egN_shapeX): (-10 to 10 number)"
1273 | }, {
1274 | token: "variable.language.modulation.envelope-generators.$1.sfz",
1275 | regex: /\beg\d{2}_time\d*(?:_oncc(?:\d{1,3})?)?\b/,
1276 | push: [{
1277 | token: "meta.opcode.sfz",
1278 | regex: /\s|$/,
1279 | next: "pop"
1280 | }, {
1281 | include: "#float_0-100"
1282 | }, {
1283 | defaultToken: "meta.opcode.sfz"
1284 | }],
1285 | comment: "opcodes: (egN_time|egN_time_onccX): (0 to 100 seconds)"
1286 | }, {
1287 | token: "variable.language.modulation.lfo.$1.sfz",
1288 | regex: /\blfo\d{2}_(?:wave|count|freq_(?:smooth|step)cc(?:\d{1,3})?)\b/,
1289 | push: [{
1290 | token: "meta.opcode.sfz",
1291 | regex: /\s|$/,
1292 | next: "pop"
1293 | }, {
1294 | include: "#int_positive"
1295 | }, {
1296 | defaultToken: "meta.opcode.sfz"
1297 | }],
1298 | comment: "opcodes: (lfoN_wave|lfoN_count|lfoN_freq|lfoN_freq_onccX|lfoN_freq_smoothccX): (positive int)"
1299 | }, {
1300 | token: "variable.language.modulation.lfo.$1.sfz",
1301 | regex: /\blfo\d{2}_freq(?:_oncc(?:\d{1,3})?)?\b/,
1302 | push: [{
1303 | token: "meta.opcode.sfz",
1304 | regex: /\s|$/,
1305 | next: "pop"
1306 | }, {
1307 | include: "#float_neg20-20"
1308 | }, {
1309 | defaultToken: "meta.opcode.sfz"
1310 | }],
1311 | comment: "opcodes: (lfoN_freq|lfoN_freq_onccN): (-20 to 20 Hz)"
1312 | }, {
1313 | token: "variable.language.modulation.lfo.$1.sfz",
1314 | regex: /\b(?:lfo\d{2}_(?:delay|fade)(?:_oncc(?:\d{1,3})?)?|count)\b/,
1315 | push: [{
1316 | token: "meta.opcode.sfz",
1317 | regex: /\s|$/,
1318 | next: "pop"
1319 | }, {
1320 | include: "#float_0-100"
1321 | }, {
1322 | defaultToken: "meta.opcode.sfz"
1323 | }],
1324 | comment: "opcodes: (lfoN_delay|lfoN_delay_onccX|lfoN_fade|lfoN_fade_onccX): (0 to 100 seconds)"
1325 | }, {
1326 | token: "variable.language.modulation.lfo.$1.sfz",
1327 | regex: /\b(?:lfo\d{2}_phase(?:_oncc(?:\d{1,3})?)?|count)\b/,
1328 | push: [{
1329 | token: "meta.opcode.sfz",
1330 | regex: /\s|$/,
1331 | next: "pop"
1332 | }, {
1333 | include: "#float_0-1"
1334 | }, {
1335 | defaultToken: "meta.opcode.sfz"
1336 | }],
1337 | comment: "opcodes: (lfoN_phase|lfoN_phase_onccX): (0 to 1 number)"
1338 | }, {
1339 | token: "variable.language.modulation.envelope-generators.$1.sfz",
1340 | regex: /\beg\d{2}_(?:(?:depth_lfo|depthadd_lfo|freq_lfo)|(?:amplitude|depth|depth_lfo|depthadd_lfo|freq_lfo|pitch|cutoff2?|eq[1-3]freq|eq[1-3]bw|eq[1-3]gain|pan|resonance2?|volume|width)(?:_oncc(?:\d{1,3})?)?)\b/,
1341 | push: [{
1342 | token: "meta.opcode.sfz",
1343 | regex: /\s|$/,
1344 | next: "pop"
1345 | }, {
1346 | include: "#float_any"
1347 | }, {
1348 | defaultToken: "meta.opcode.sfz"
1349 | }],
1350 | comment: "opcodes: (other eg destinations): (any float)"
1351 | }, {
1352 | token: "variable.language.modulation.lfo.$1.sfz",
1353 | regex: /\blfo\d{2}_(?:(?:depth_lfo|depthadd_lfo|freq_lfo)|(?:amplitude|decim|bitred|depth_lfo|depthadd_lfo|freq_lfo|pitch|cutoff2?|eq[1-3]freq|eq[1-3]bw|eq[1-3]gain|pan|resonance2?|volume|width)(?:_oncc(?:\d{1,3})?)?)\b/,
1354 | push: [{
1355 | token: "meta.opcode.sfz",
1356 | regex: /\s|$/,
1357 | next: "pop"
1358 | }, {
1359 | include: "#float_any"
1360 | }, {
1361 | defaultToken: "meta.opcode.sfz"
1362 | }],
1363 | comment: "opcodes: (other lfo destinations): (any float)"
1364 | }],
1365 | "#sfz2_curves": [{
1366 | token: "variable.language.curves.$1.sfz",
1367 | regex: /\bv[0-9]{3}\b/,
1368 | push: [{
1369 | token: "meta.opcode.sfz",
1370 | regex: /\s|$/,
1371 | next: "pop"
1372 | }, {
1373 | include: "#float_0-1"
1374 | }, {
1375 | defaultToken: "meta.opcode.sfz"
1376 | }],
1377 | comment: "opcodes: (vN): (0 to 1 number)"
1378 | }],
1379 | "#aria_instrument-settings": [{
1380 | token: "variable.language.instrument-settings.$1.sfz",
1381 | regex: /\bhint_[A-z_]*\b/,
1382 | push: [{
1383 | token: "meta.opcode.sfz",
1384 | regex: /\s|$/,
1385 | next: "pop"
1386 | }, {
1387 | include: "#float_any"
1388 | }, {
1389 | defaultToken: "meta.opcode.sfz"
1390 | }],
1391 | comment: "opcodes: (hint_): (any number)"
1392 | }, {
1393 | token: "variable.language.instrument-settings.$1.sfz",
1394 | regex: /\b(?:set_|lo|hi)hdcc(?:\d{1,3})?\b/,
1395 | push: [{
1396 | token: "meta.opcode.sfz",
1397 | regex: /\s|$/,
1398 | next: "pop"
1399 | }, {
1400 | include: "#float_any"
1401 | }, {
1402 | defaultToken: "meta.opcode.sfz"
1403 | }],
1404 | comment: "opcodes: (set_hdccN|lohdccN|hihdccN): (any number)"
1405 | }, {
1406 | token: "variable.language.instrument-settings.$1.sfz",
1407 | regex: /\b(?:sustain_cc|sostenuto_cc|sustain_lo|sostenuto_lo)\b/,
1408 | push: [{
1409 | token: "meta.opcode.sfz",
1410 | regex: /\s|$/,
1411 | next: "pop"
1412 | }, {
1413 | include: "#int_0-127"
1414 | }, {
1415 | defaultToken: "meta.opcode.sfz"
1416 | }],
1417 | comment: "opcodes: (sustain_cc|sostenuto_cc|sustain_lo|sostenuto_lo): (0 to 127 MIDI byte)"
1418 | }, {
1419 | token: "variable.language.instrument-settings.$1.sfz",
1420 | regex: /\bsw_octave_offset\b/,
1421 | push: [{
1422 | token: "meta.opcode.sfz",
1423 | regex: /\s|$/,
1424 | next: "pop"
1425 | }, {
1426 | include: "#int_neg10-10"
1427 | }, {
1428 | defaultToken: "meta.opcode.sfz"
1429 | }],
1430 | comment: "opcodes: (sw_octave_offset): (-10 to 10 octaves)"
1431 | }, {
1432 | token: "variable.language.instrument-settings.voice-lifecycle.$1.sfz",
1433 | regex: /\boff_curve\b/,
1434 | push: [{
1435 | token: "meta.opcode.sfz",
1436 | regex: /\s|$/,
1437 | next: "pop"
1438 | }, {
1439 | include: "#int_positive"
1440 | }, {
1441 | defaultToken: "meta.opcode.sfz"
1442 | }],
1443 | comment: "opcodes: (off_curve): (0 to any curve)"
1444 | }, {
1445 | token: "variable.language.instrument-settings.voice-lifecycle.$1.sfz",
1446 | regex: /\b(?:off_shape|off_time)\b/,
1447 | push: [{
1448 | token: "meta.opcode.sfz",
1449 | regex: /\s|$/,
1450 | next: "pop"
1451 | }, {
1452 | include: "#float_neg10-10"
1453 | }, {
1454 | defaultToken: "meta.opcode.sfz"
1455 | }],
1456 | comment: "opcodes: (off_shape|off_time): (-10 to 10 number)"
1457 | }],
1458 | "#aria_region-logic": [{
1459 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
1460 | regex: /\b(?:sw_default|sw_lolast|sw_hilast)\b/,
1461 | push: [{
1462 | token: "meta.opcode.sfz",
1463 | regex: /\s|$/,
1464 | next: "pop"
1465 | }, {
1466 | include: "#int_0-127"
1467 | }, {
1468 | defaultToken: "meta.opcode.sfz"
1469 | }],
1470 | comment: "opcodes: (sw_default|sw_lolast|sw_hilast): (0 to 127 MIDI Note)"
1471 | }, {
1472 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
1473 | regex: /\bsw_label\b/,
1474 | push: [{
1475 | token: "meta.opcode.sfz",
1476 | regex: /\s|$/,
1477 | next: "pop"
1478 | }, {
1479 | include: "#string_any_continuous"
1480 | }, {
1481 | defaultToken: "meta.opcode.sfz"
1482 | }],
1483 | comment: "opcodes: (sw_label): (any string)"
1484 | }, {
1485 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
1486 | regex: /\bvar\d{2}_curvecc(?:\d{1,3})?\b/,
1487 | push: [{
1488 | token: "meta.opcode.sfz",
1489 | regex: /\s|$/,
1490 | next: "pop"
1491 | }, {
1492 | include: "#int_positive"
1493 | }, {
1494 | defaultToken: "meta.opcode.sfz"
1495 | }],
1496 | comment: "opcodes: (varNN_curveccX): (0 to any curve)"
1497 | }, {
1498 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
1499 | regex: /\bvar\d{2}_mod\b/,
1500 | push: [{
1501 | token: "meta.opcode.sfz",
1502 | regex: /\s|$/,
1503 | next: "pop"
1504 | }, {
1505 | include: "#string_add-mult"
1506 | }, {
1507 | defaultToken: "meta.opcode.sfz"
1508 | }],
1509 | comment: "opcodes: (varNN_mod): (add|mult)"
1510 | }, {
1511 | token: "variable.language.region-logic.midi-conditions.$1.sfz",
1512 | regex: /\b(?:var\d{2}_oncc(?:\d{1,3})?|var\d{2}_(?:pitch|cutoff|resonance|cutoff2|resonance2|eq[1-3]freq|eq[1-3]bw|eq[1-3]gain|volume|amplitude|pan|width))\b/,
1513 | push: [{
1514 | token: "meta.opcode.sfz",
1515 | regex: /\s|$/,
1516 | next: "pop"
1517 | }, {
1518 | include: "#float_any"
1519 | }, {
1520 | defaultToken: "meta.opcode.sfz"
1521 | }],
1522 | comment: "opcodes: (varNN_onccX|varNN_target): (any float)"
1523 | }],
1524 | "#aria_performance-parameters": [{
1525 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
1526 | regex: /\b(?:amplitude|amplitude_oncc(?:\d{1,3})?|global_amplitude|master_amplitude|group_amplitude)\b/,
1527 | push: [{
1528 | token: "meta.opcode.sfz",
1529 | regex: /\s|$/,
1530 | next: "pop"
1531 | }, {
1532 | include: "#float_0-100"
1533 | }, {
1534 | defaultToken: "meta.opcode.sfz"
1535 | }],
1536 | comment: "opcodes: (amplitude|amplitude_onccN|global_amplitude|master_amplitude|group_amplitude): (0 to 100 percent)"
1537 | }, {
1538 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
1539 | regex: /\bamplitude_curvecc(?:\d{1,3})?\b/,
1540 | push: [{
1541 | token: "meta.opcode.sfz",
1542 | regex: /\s|$/,
1543 | next: "pop"
1544 | }, {
1545 | include: "#int_positive"
1546 | }, {
1547 | defaultToken: "meta.opcode.sfz"
1548 | }],
1549 | comment: "opcodes: (amplitude_curveccN): (any positive curve)"
1550 | }, {
1551 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
1552 | regex: /\bamplitude_smoothcc(?:\d{1,3})?\b/,
1553 | push: [{
1554 | token: "meta.opcode.sfz",
1555 | regex: /\s|$/,
1556 | next: "pop"
1557 | }, {
1558 | include: "#int_0-9600"
1559 | }, {
1560 | defaultToken: "meta.opcode.sfz"
1561 | }],
1562 | comment: "opcodes: (amplitude_smoothccN): (0 to 9600 number)"
1563 | }, {
1564 | token: "variable.language.performance-parameters.amplifier.$1.sfz",
1565 | regex: /\bpan_law\b/,
1566 | push: [{
1567 | token: "meta.opcode.sfz",
1568 | regex: /\s|$/,
1569 | next: "pop"
1570 | }, {
1571 | include: "#string_balance-mma"
1572 | }, {
1573 | defaultToken: "meta.opcode.sfz"
1574 | }],
1575 | comment: "opcodes: (pan_law): (balance|mma)"
1576 | }, {
1577 | token: "variable.language.performance-parameters.amplifiers.$1.sfz",
1578 | regex: /\b(?:global_volume|master_volume|group_volume|volume_oncc(?:\d{1,3})?)\b/,
1579 | push: [{
1580 | token: "meta.opcode.sfz",
1581 | regex: /\s|$/,
1582 | next: "pop"
1583 | }, {
1584 | include: "#float_neg144-6"
1585 | }, {
1586 | defaultToken: "meta.opcode.sfz"
1587 | }],
1588 | comment: "opcodes: (global_volume|master_volume|group_volume|volume_onccN): (-144 to 6 dB)"
1589 | }],
1590 | "#aria_modulation": [{
1591 | token: "variable.language.modulation.envelope-generators.$1.sfz",
1592 | regex: /\b(?:ampeg_attack_shape|ampeg_decay_shape|ampeg_release_shape|eg\d{2}_shape\d+)\b/,
1593 | push: [{
1594 | token: "meta.opcode.sfz",
1595 | regex: /\s|$/,
1596 | next: "pop"
1597 | }, {
1598 | include: "#float_neg10-10"
1599 | }, {
1600 | defaultToken: "meta.opcode.sfz"
1601 | }],
1602 | comment: "opcodes: (ampeg_attack_shape|ampeg_decay_shape|ampeg_release_shape|egN_shapeX): (-10 to 10 float)"
1603 | }, {
1604 | token: "variable.language.modulation.envelope-generators.$1.sfz",
1605 | regex: /\b(?:ampeg_release_zero|ampeg_decay_zero)\b/,
1606 | push: [{
1607 | token: "meta.opcode.sfz",
1608 | regex: /\s|$/,
1609 | next: "pop"
1610 | }, {
1611 | include: "#string_on-off"
1612 | }, {
1613 | defaultToken: "meta.opcode.sfz"
1614 | }],
1615 | comment: "opcodes: (ampeg_release_zero|ampeg_decay_zero): (true|false)"
1616 | }, {
1617 | token: "variable.language.modulation.lfo.$1.sfz",
1618 | regex: /\blfo\d{2}_(?:offset|ratio|scale)2?\b/,
1619 | push: [{
1620 | token: "meta.opcode.sfz",
1621 | regex: /\s|$/,
1622 | next: "pop"
1623 | }, {
1624 | include: "#float_any"
1625 | }, {
1626 | defaultToken: "meta.opcode.sfz"
1627 | }],
1628 | comment: "opcodes: (lfoN_offset|lfoN_offset2|lfoN_ratio|lfoN_ratio2|lfoN_scale|lfoN_scale2): (any float)"
1629 | }, {
1630 | token: "variable.language.modulation.lfo.$1.sfz",
1631 | regex: /\blfo\d{2}_wave2?\b/,
1632 | push: [{
1633 | token: "meta.opcode.sfz",
1634 | regex: /\s|$/,
1635 | next: "pop"
1636 | }, {
1637 | include: "#int_0-127"
1638 | }, {
1639 | defaultToken: "meta.opcode.sfz"
1640 | }],
1641 | comment: "opcodes: (lfoN_wave|lfoN_wav2): (0 to 127 MIDI Number)"
1642 | }],
1643 | "#aria_curves": [{
1644 | token: "variable.language.curves.$1.sfz",
1645 | regex: /\bcurve_index\b/,
1646 | push: [{
1647 | token: "meta.opcode.sfz",
1648 | regex: /\s|$/,
1649 | next: "pop"
1650 | }, {
1651 | include: "#int_positive"
1652 | }, {
1653 | defaultToken: "meta.opcode.sfz"
1654 | }],
1655 | comment: "opcodes: (curve_index): (any positive integer)"
1656 | }],
1657 | "#aria_effects": [{
1658 | token: "variable.language.effects.$1.sfz",
1659 | regex: /\bparam_offset\b/,
1660 | push: [{
1661 | token: "meta.opcode.sfz",
1662 | regex: /\s|$/,
1663 | next: "pop"
1664 | }, {
1665 | include: "#int_any"
1666 | }, {
1667 | defaultToken: "meta.opcode.sfz"
1668 | }],
1669 | comment: "opcodes: (param_offset): (any integer)"
1670 | }, {
1671 | token: "variable.language.effects.$1.sfz",
1672 | regex: /\bvendor_specific\b/,
1673 | push: [{
1674 | token: "meta.opcode.sfz",
1675 | regex: /\s|$/,
1676 | next: "pop"
1677 | }, {
1678 | include: "#string_any_continuous"
1679 | }, {
1680 | defaultToken: "meta.opcode.sfz"
1681 | }],
1682 | comment: "opcodes: (vendor_specific): (any to continuous string)"
1683 | }],
1684 | "#float_neg30000-30000": [{
1685 | token: [
1686 | "keyword.operator.assignment.sfz",
1687 | "constant.numeric.float.sfz"
1688 | ],
1689 | regex: /(=)(-?(? indent)
2327 | break;
2328 | var subRange = this.getFoldWidgetRange(session, "all", row);
2329 | if (subRange) {
2330 | if (subRange.start.row <= startRow) {
2331 | break;
2332 | }
2333 | else if (subRange.isMultiLine()) {
2334 | row = subRange.end.row;
2335 | }
2336 | else if (startIndent == indent) {
2337 | break;
2338 | }
2339 | }
2340 | endRow = row;
2341 | }
2342 | return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
2343 | };
2344 | this.getCommentRegionBlock = function (session, line, row) {
2345 | var startColumn = line.search(/\s*$/);
2346 | var maxRow = session.getLength();
2347 | var startRow = row;
2348 | var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
2349 | var depth = 1;
2350 | while (++row < maxRow) {
2351 | line = session.getLine(row);
2352 | var m = re.exec(line);
2353 | if (!m)
2354 | continue;
2355 | if (m[1])
2356 | depth--;
2357 | else
2358 | depth++;
2359 | if (!depth)
2360 | break;
2361 | }
2362 | var endRow = row;
2363 | if (endRow > startRow) {
2364 | return new Range(startRow, startColumn, endRow, line.length);
2365 | }
2366 | };
2367 | }).call(FoldMode.prototype);
2368 |
2369 | });
2370 |
2371 | define("ace/mode/sfz",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/sfz_highlight_rules","ace/mode/folding/cstyle"], function(require, exports, module){/* ***** BEGIN LICENSE BLOCK *****
2372 | * Distributed under the BSD license:
2373 | *
2374 | * Copyright (c) 2012, Ajax.org B.V.
2375 | * All rights reserved.
2376 | *
2377 | * Redistribution and use in source and binary forms, with or without
2378 | * modification, are permitted provided that the following conditions are met:
2379 | * * Redistributions of source code must retain the above copyright
2380 | * notice, this list of conditions and the following disclaimer.
2381 | * * Redistributions in binary form must reproduce the above copyright
2382 | * notice, this list of conditions and the following disclaimer in the
2383 | * documentation and/or other materials provided with the distribution.
2384 | * * Neither the name of Ajax.org B.V. nor the
2385 | * names of its contributors may be used to endorse or promote products
2386 | * derived from this software without specific prior written permission.
2387 | *
2388 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
2389 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2390 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2391 | * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
2392 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2393 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2394 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2395 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2396 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2397 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2398 | *
2399 | * ***** END LICENSE BLOCK ***** */
2400 | "use strict";
2401 | var oop = require("../lib/oop");
2402 | var TextMode = require("./text").Mode;
2403 | var SFZHighlightRules = require("./sfz_highlight_rules").SFZHighlightRules;
2404 | var FoldMode = require("./folding/cstyle").FoldMode;
2405 | var Mode = function () {
2406 | this.HighlightRules = SFZHighlightRules;
2407 | this.foldingRules = new FoldMode();
2408 | };
2409 | oop.inherits(Mode, TextMode);
2410 | (function () {
2411 | this.$id = "ace/mode/sfz";
2412 | }).call(Mode.prototype);
2413 | exports.Mode = Mode;
2414 |
2415 | }); (function() {
2416 | window.require(["ace/mode/sfz"], function(m) {
2417 | if (typeof module == "object" && typeof exports == "object" && module) {
2418 | module.exports = m;
2419 | }
2420 | });
2421 | })();
2422 |
--------------------------------------------------------------------------------