- Notifications
You must be signed in to change notification settings - Fork 6
Add example code for WAT parsing and server-side plugins #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
19 commits Select commit Hold shift + click to select a range
b477176 Split host and guest packages
MaxDesiatov b0f230e fix up formatting in *.wat files
MaxDesiatov 999d656 Update `$print` param comments in `demo.wat`
MaxDesiatov d628840 Formatting cleanups
MaxDesiatov 278ef3e Use Hummingbird as static server
MaxDesiatov 6d812ae Clean up formatting, serve HTML
MaxDesiatov 118a549 Allow multiple canvas contexts in guest code
MaxDesiatov 5226840 Allow multiple audio elements in `Guest`
MaxDesiatov b3f13e5 Implement rendering of multiple modules in HTML
MaxDesiatov b878df2 Compute module paths dynamically in Guest's `index.js`
MaxDesiatov 898d79d Split `plotter` and `swift-audio` into separate modules
MaxDesiatov d097808 Dynamically link `plotter` module in `index.js`
MaxDesiatov cbbb8b1 Fix multiple synth plugins loading in Guest's `index.js`
MaxDesiatov 1e13b6f Add mixed audio endpoint
MaxDesiatov 5c1bb52 Clean up formatting
MaxDesiatov 967916f Simplify Host server router
MaxDesiatov 8d43cbf Refactor `MixedOutput` module results iteration
MaxDesiatov 9dbdfab Split WATExample and ServerHost, implement file uploads
MaxDesiatov 51bef99 Clean up formatting
MaxDesiatov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../Shared |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift open source project | ||
| // | ||
| // Copyright (c) 2024 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| | ||
| @_expose(wasm, "main") | ||
| @_cdecl("main") | ||
| func main(contextIndex: Int) { | ||
| var sequencedBass = Sequencer( | ||
| instrument: Bass(), | ||
| sequence: [ | ||
| .noteOn(.c.octave(1)), .noteOff, .noteOn(.d.octave(1)), .noteOff, .noteOn(.e.octave(1)), | ||
| .noteOff, .noteOn(.f.octave(1)), .noteOff, .noteOn(.g.octave(1)), .noteOff, .noteOn(.a.octave(1)), | ||
| ], | ||
| stepLengthInSeconds: 0.25 | ||
| ) | ||
| | ||
| let totalLengthInSeconds = 6 | ||
| | ||
| let buffer = AudioBuffer( | ||
| capacity: sampleRate * totalLengthInSeconds, | ||
| source: &sequencedBass | ||
| ) | ||
| | ||
| Audio.encode(contextIndex: contextIndex, buffer) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../Shared |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift open source project | ||
| // | ||
| // Copyright (c) 2024 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| | ||
| @_expose(wasm, "main") | ||
| @_cdecl("main") | ||
| func main(contextIndex: Int) { | ||
| var sequencedHiHat = Sequencer( | ||
| instrument: HiHat(), | ||
| sequence: [ | ||
| .noteOff, .noteOff, .noteOff, .noteOff, | ||
| .noteOff, .noteOff, .noteOff, .noteOff, | ||
| .noteOn(.c.octave(1)), .noteOff, .noteOn(.c.octave(1)), .noteOff, | ||
| .noteOff, .noteOff, .noteOff, .noteOff, | ||
| ], | ||
| stepLengthInSeconds: 0.125 | ||
| ) | ||
| | ||
| let totalLengthInSeconds = 6 | ||
| | ||
| let buffer = AudioBuffer( | ||
| capacity: sampleRate * totalLengthInSeconds, | ||
| source: &sequencedHiHat | ||
| ) | ||
| | ||
| Audio.encode(contextIndex: contextIndex, buffer) | ||
| } |
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift open source project | ||
| // | ||
| // Copyright (c) 2024 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| | ||
| import { Encoder } from './encoder.js'; | ||
| | ||
| const decoder = new TextDecoder(); | ||
| const loggerElement = document.getElementById('wasm-logger'); | ||
| const moduleInstances = []; | ||
| | ||
| function wasmMemoryAsString(i, address, byteCount) { | ||
| return decoder.decode(moduleInstances[i].exports.memory.buffer.slice(address, address + byteCount)); | ||
| } | ||
| | ||
| function wasmMemoryAsFloat32Array(i, address, byteCount) { | ||
| return new Float32Array(moduleInstances[i].exports.memory.buffer.slice(address, address + byteCount)); | ||
| } | ||
| | ||
| const contexts = []; | ||
| | ||
| const canvasImports = { | ||
| canvas: { | ||
| beginPath: (i) => contexts[i].beginPath(), | ||
| stroke: (i) => contexts[i].stroke(), | ||
| moveTo: (i, x, y) => contexts[i].moveTo(x, y), | ||
| lineTo: (i, x, y) => contexts[i].lineTo(x, y), | ||
| }, | ||
| } | ||
| | ||
| const plotterModule = await WebAssembly.instantiateStreaming( | ||
| fetch(".build/wasm32-unknown-none-wasm/release/plotter.wasm"), | ||
| { ...canvasImports } | ||
| ); | ||
| | ||
| function encodeAndPlot(audioBuffer, context) { | ||
| const wavEncoder = new Encoder(44100, 1); | ||
| wavEncoder.encode([audioBuffer]); | ||
| const blob = wavEncoder.finish(); | ||
| | ||
| const audioURL = URL.createObjectURL(blob); | ||
| document.getElementsByClassName('audio')[context].setAttribute('src', audioURL); | ||
| | ||
| const byteCount = audioBuffer.length * 4; | ||
| const bufferPointer = plotterExports.allocateAudioBuffer(byteCount); | ||
| const memoryBytes = new Float32Array(plotterExports.memory.buffer); | ||
| memoryBytes.set(audioBuffer, bufferPointer / 4); | ||
| plotterExports.plot(context, 1000, 200, 10, bufferPointer, byteCount); | ||
| plotterExports.free(bufferPointer); | ||
| } | ||
| | ||
| const plotterExports = plotterModule.instance.exports; | ||
| | ||
| const audioImports = { | ||
| audio: { | ||
| encode: (i, address, byteCount) => { | ||
| const audioBuffer = wasmMemoryAsFloat32Array(i, address, byteCount); | ||
| | ||
| encodeAndPlot(audioBuffer, i); | ||
| }, | ||
| }, | ||
| }; | ||
| | ||
| const consoleImports = { | ||
| console: { | ||
| log: (i, address, byteCount) => { | ||
| loggerElement.innerHTML = wasmMemoryAsString(i, address, byteCount); | ||
| }, | ||
| logInt: (x) => console.log(x), | ||
| logFloat: (x) => console.log(x), | ||
| } | ||
| }; | ||
| | ||
| const pluginElements = document.getElementsByClassName("plugin"); | ||
| | ||
| for (let i = 0; i < pluginElements.length; ++i) { | ||
| const element = pluginElements[i]; | ||
| const canvasElement = element.getElementsByClassName("plotter")[0]; | ||
| const canvasContext = canvasElement.getContext('2d'); | ||
| canvasContext.strokeStyle = 'white'; | ||
| | ||
| contexts.push(canvasContext); | ||
| | ||
| const { instance } = await WebAssembly.instantiateStreaming( | ||
| fetch(element.dataset.modulePath), | ||
| { ...audioImports, ...consoleImports } | ||
| ); | ||
| | ||
| moduleInstances.push(instance); | ||
| | ||
| instance.exports.main(i); | ||
| } | ||
| | ||
| const mixElement = document.getElementById("tracks-mix"); | ||
| | ||
| if (mixElement) { | ||
| const canvasElement = mixElement.getElementsByClassName("plotter")[0]; | ||
| const canvasContext = canvasElement.getContext('2d'); | ||
| canvasContext.strokeStyle = 'white'; | ||
| | ||
| contexts.push(canvasContext); | ||
| | ||
| const response = await fetch("/mix"); | ||
| const responseBlob = await response.blob(); | ||
| | ||
| const audioBuffer = new Float32Array(await responseBlob.arrayBuffer()); | ||
| | ||
| encodeAndPlot(audioBuffer, contexts.length - 1); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../Shared |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // This source file is part of the Swift open source project | ||
| // | ||
| // Copyright (c) 2024 Apple Inc. and the Swift project authors | ||
| // Licensed under Apache License v2.0 with Runtime Library Exception | ||
| // | ||
| // See https://swift.org/LICENSE.txt for license information | ||
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| | ||
| @_expose(wasm, "main") | ||
| @_cdecl("main") | ||
| func main(contextIndex: Int) { | ||
| var sequencedKick = Sequencer( | ||
| instrument: Kick(), | ||
| sequence: [.noteOff, .noteOn(.c.octave(1)), .noteOff, .noteOn(.c.octave(1))], | ||
| stepLengthInSeconds: 0.25 | ||
| ) | ||
| | ||
| let totalLengthInSeconds = 6 | ||
| | ||
| let buffer = AudioBuffer( | ||
| capacity: sampleRate * totalLengthInSeconds, | ||
| source: &sequencedKick | ||
| ) | ||
| | ||
| Audio.encode(contextIndex: contextIndex, buffer) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ../Shared |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented in a separate dynamically-linked
PlotterWasm module.