-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
3,442 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains 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,23 @@ | ||
### Auto-generated HTML & Javascript for Cmajor Patch "Freeverb" | ||
|
||
This folder contains some self-contained HTML/Javascript files that play and show a Cmajor | ||
patch using WebAssembly and WebAudio. | ||
|
||
For `index.html` to display correctly, this folder needs to be served as HTTP, so if you're | ||
running it locally, you'll need to start a webserver that serves this folder, and then | ||
point your browser at whatever URL your webserver provides. For example, you could run | ||
`python3 -m http.server` in this folder, and then browse to the address it chooses. | ||
|
||
The files have all been generated using the Cmajor command-line tool: | ||
``` | ||
cmaj generate --target=webaudio --output=<location of this folder> <path to the .cmajorpatch file to convert> | ||
``` | ||
|
||
- `index.html` is a minimal page that creates the javascript object that implements the patch, | ||
connects it to the default audio and MIDI devices, and displays its view. | ||
- `Freeverb.js` - this is the Javascript wrapper class for the patch, encapsulating its | ||
DSP as webassembly, and providing an API that is used to both render the audio and | ||
control its properties. | ||
- `cmaj_api` - this folder contains javascript helper modules and resources. | ||
|
||
To learn more about Cmajor, visit [cmajor.dev](cmajor.dev) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains 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,112 @@ | ||
// | ||
// ,ad888ba, 88 | ||
// d8"' "8b | ||
// d8 88,dba,,adba, ,aPP8A.A8 88 | ||
// Y8, 88 88 88 88 88 88 | ||
// Y8a. .a8P 88 88 88 88, ,88 88 (C)2024 Cmajor Software Ltd | ||
// '"Y888Y"' 88 88 88 '"8bbP"Y8 88 https://cmajor.dev | ||
// ,88 | ||
// 888P" | ||
// | ||
// This file may be used under the terms of the ISC license: | ||
// | ||
// Permission to use, copy, modify, and/or distribute this software for any purpose with or | ||
// without fee is hereby granted, provided that the above copyright notice and this permission | ||
// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | ||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR | ||
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | ||
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
|
||
|
||
/** This event listener management class allows listeners to be attached and | ||
* removed from named event types. | ||
*/ | ||
export class EventListenerList | ||
{ | ||
constructor() | ||
{ | ||
this.listenersPerType = {}; | ||
} | ||
|
||
/** Adds a listener for a specifc event type. | ||
* If the listener is already registered, this will simply add it again. | ||
* Each call to addEventListener() must be paired with a removeventListener() | ||
* call to remove it. | ||
* | ||
* @param {string} type | ||
*/ | ||
addEventListener (type, listener) | ||
{ | ||
if (type && listener) | ||
{ | ||
const list = this.listenersPerType[type]; | ||
|
||
if (list) | ||
list.push (listener); | ||
else | ||
this.listenersPerType[type] = [listener]; | ||
} | ||
} | ||
|
||
/** Removes a listener that was previously added for the given event type. | ||
* @param {string} type | ||
*/ | ||
removeEventListener (type, listener) | ||
{ | ||
if (type && listener) | ||
{ | ||
const list = this.listenersPerType[type]; | ||
|
||
if (list) | ||
{ | ||
const i = list.indexOf (listener); | ||
|
||
if (i >= 0) | ||
list.splice (i, 1); | ||
} | ||
} | ||
} | ||
|
||
/** Attaches a callback function that will be automatically unregistered | ||
* the first time it is invoked. | ||
* | ||
* @param {string} type | ||
*/ | ||
addSingleUseListener (type, listener) | ||
{ | ||
const l = message => | ||
{ | ||
this.removeEventListener (type, l); | ||
listener?.(message); | ||
}; | ||
|
||
this.addEventListener (type, l); | ||
} | ||
|
||
/** Synchronously dispatches an event object to all listeners | ||
* that are registered for the given type. | ||
* | ||
* @param {string} type | ||
*/ | ||
dispatchEvent (type, event) | ||
{ | ||
const list = this.listenersPerType[type]; | ||
|
||
if (list) | ||
for (const listener of list) | ||
listener?.(event); | ||
} | ||
|
||
/** Returns the number of listeners that are currently registered | ||
* for the given type of event. | ||
* | ||
* @param {string} type | ||
*/ | ||
getNumListenersForType (type) | ||
{ | ||
const list = this.listenersPerType[type]; | ||
return list ? list.length : 0; | ||
} | ||
} |
This file contains 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,178 @@ | ||
// | ||
// ,ad888ba, 88 | ||
// d8"' "8b | ||
// d8 88,dba,,adba, ,aPP8A.A8 88 | ||
// Y8, 88 88 88 88 88 88 | ||
// Y8a. .a8P 88 88 88 88, ,88 88 (C)2024 Cmajor Software Ltd | ||
// '"Y888Y"' 88 88 88 '"8bbP"Y8 88 https://cmajor.dev | ||
// ,88 | ||
// 888P" | ||
// | ||
// This file may be used under the terms of the ISC license: | ||
// | ||
// Permission to use, copy, modify, and/or distribute this software for any purpose with or | ||
// without fee is hereby granted, provided that the above copyright notice and this permission | ||
// notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | ||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY | ||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR | ||
// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | ||
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
|
||
import * as Controls from "/cmaj_api/cmaj-parameter-controls.js" | ||
|
||
//============================================================================== | ||
/** A simple, generic view which can control any type of patch */ | ||
class GenericPatchView extends HTMLElement | ||
{ | ||
/** Creates a view for a patch. | ||
* @param {PatchConnection} patchConnection - the connection to the target patch | ||
*/ | ||
constructor (patchConnection) | ||
{ | ||
super(); | ||
|
||
this.patchConnection = patchConnection; | ||
|
||
this.statusListener = status => | ||
{ | ||
this.status = status; | ||
this.createControlElements(); | ||
}; | ||
|
||
this.attachShadow ({ mode: "open" }); | ||
this.shadowRoot.innerHTML = this.getHTML(); | ||
|
||
this.titleElement = this.shadowRoot.getElementById ("patch-title"); | ||
this.parametersElement = this.shadowRoot.getElementById ("patch-parameters"); | ||
} | ||
|
||
//============================================================================== | ||
/** @private */ | ||
connectedCallback() | ||
{ | ||
this.patchConnection.addStatusListener (this.statusListener); | ||
this.patchConnection.requestStatusUpdate(); | ||
} | ||
|
||
/** @private */ | ||
disconnectedCallback() | ||
{ | ||
this.patchConnection.removeStatusListener (this.statusListener); | ||
} | ||
|
||
/** @private */ | ||
createControlElements() | ||
{ | ||
this.parametersElement.innerHTML = ""; | ||
this.titleElement.innerText = this.status?.manifest?.name ?? "Cmajor"; | ||
|
||
for (const endpointInfo of this.status?.details?.inputs) | ||
{ | ||
if (! endpointInfo.annotation?.hidden) | ||
{ | ||
const control = Controls.createLabelledControl (this.patchConnection, endpointInfo); | ||
|
||
if (control) | ||
this.parametersElement.appendChild (control); | ||
} | ||
} | ||
} | ||
|
||
/** @private */ | ||
getHTML() | ||
{ | ||
return ` | ||
<style> | ||
* { | ||
box-sizing: border-box; | ||
user-select: none; | ||
-webkit-user-select: none; | ||
-moz-user-select: none; | ||
-ms-user-select: none; | ||
font-family: Avenir, 'Avenir Next LT Pro', Montserrat, Corbel, 'URW Gothic', source-sans-pro, sans-serif; | ||
font-size: 0.9rem; | ||
} | ||
:host { | ||
--header-height: 2.5rem; | ||
--foreground: #ffffff; | ||
--background: #1a1a1a; | ||
display: block; | ||
height: 100%; | ||
background-color: var(--background); | ||
} | ||
.main { | ||
background: var(--background); | ||
height: 100%; | ||
} | ||
.header { | ||
width: 100%; | ||
height: var(--header-height); | ||
border-bottom: 0.1rem solid var(--foreground); | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
} | ||
#patch-title { | ||
color: var(--foreground); | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
overflow: hidden; | ||
cursor: default; | ||
font-size: 140%; | ||
} | ||
.logo { | ||
flex: 1; | ||
height: 80%; | ||
margin-left: 0.3rem; | ||
margin-right: 0.3rem; | ||
background-color: var(--foreground); | ||
mask: url(cmaj_api/assets/cmajor-logo.svg); | ||
mask-repeat: no-repeat; | ||
-webkit-mask: url(cmaj_api/assets/cmajor-logo.svg); | ||
-webkit-mask-repeat: no-repeat; | ||
min-width: 6.25rem; | ||
} | ||
.header-filler { | ||
flex: 1; | ||
} | ||
#patch-parameters { | ||
height: calc(100% - var(--header-height)); | ||
overflow: auto; | ||
padding: 1rem; | ||
text-align: center; | ||
} | ||
${Controls.getAllCSS()} | ||
</style> | ||
<div class="main"> | ||
<div class="header"> | ||
<span class="logo"></span> | ||
<h2 id="patch-title"></h2> | ||
<div class="header-filler"></div> | ||
</div> | ||
<div id="patch-parameters"></div> | ||
</div>`; | ||
} | ||
} | ||
|
||
window.customElements.define ("cmaj-generic-patch-view", GenericPatchView); | ||
|
||
//============================================================================== | ||
/** Creates a generic view element which can be used to control any patch. | ||
* @param {PatchConnection} patchConnection - the connection to the target patch | ||
*/ | ||
export default function createPatchView (patchConnection) | ||
{ | ||
return new GenericPatchView (patchConnection); | ||
} |
Oops, something went wrong.