| @@ -11,6 +11,7 @@ | |||||
| "@vueform/slider": "^2.0.5", | "@vueform/slider": "^2.0.5", | ||||
| "@vueform/toggle": "^2.0.1", | "@vueform/toggle": "^2.0.1", | ||||
| "core-js": "^3.6.5", | "core-js": "^3.6.5", | ||||
| "mobile-drag-drop": "^2.3.0-rc.2", | |||||
| "postcss": "^8.3.6", | "postcss": "^8.3.6", | ||||
| "reflect-metadata": "^0.1.13", | "reflect-metadata": "^0.1.13", | ||||
| "vue": "^3.0.0", | "vue": "^3.0.0", | ||||
| @@ -11036,6 +11037,11 @@ | |||||
| "mkdirp": "bin/cmd.js" | "mkdirp": "bin/cmd.js" | ||||
| } | } | ||||
| }, | }, | ||||
| "node_modules/mobile-drag-drop": { | |||||
| "version": "2.3.0-rc.2", | |||||
| "resolved": "https://registry.npmjs.org/mobile-drag-drop/-/mobile-drag-drop-2.3.0-rc.2.tgz", | |||||
| "integrity": "sha512-4rHP0PUeWkSp0O3waNHPQZCHeZnLu8bE59MerWOnZJ249BCyICXL1WWp3xqkMKXEDFYuhfk3bS42bKB9IeN9uw==" | |||||
| }, | |||||
| "node_modules/move-concurrently": { | "node_modules/move-concurrently": { | ||||
| "version": "1.0.1", | "version": "1.0.1", | ||||
| "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz", | "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz", | ||||
| @@ -26963,6 +26969,11 @@ | |||||
| "minimist": "^1.2.5" | "minimist": "^1.2.5" | ||||
| } | } | ||||
| }, | }, | ||||
| "mobile-drag-drop": { | |||||
| "version": "2.3.0-rc.2", | |||||
| "resolved": "https://registry.npmjs.org/mobile-drag-drop/-/mobile-drag-drop-2.3.0-rc.2.tgz", | |||||
| "integrity": "sha512-4rHP0PUeWkSp0O3waNHPQZCHeZnLu8bE59MerWOnZJ249BCyICXL1WWp3xqkMKXEDFYuhfk3bS42bKB9IeN9uw==" | |||||
| }, | |||||
| "move-concurrently": { | "move-concurrently": { | ||||
| "version": "1.0.1", | "version": "1.0.1", | ||||
| "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz", | "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz", | ||||
| @@ -11,6 +11,7 @@ | |||||
| "@vueform/slider": "^2.0.5", | "@vueform/slider": "^2.0.5", | ||||
| "@vueform/toggle": "^2.0.1", | "@vueform/toggle": "^2.0.1", | ||||
| "core-js": "^3.6.5", | "core-js": "^3.6.5", | ||||
| "mobile-drag-drop": "^2.3.0-rc.2", | |||||
| "postcss": "^8.3.6", | "postcss": "^8.3.6", | ||||
| "reflect-metadata": "^0.1.13", | "reflect-metadata": "^0.1.13", | ||||
| "vue": "^3.0.0", | "vue": "^3.0.0", | ||||
| @@ -52,6 +52,15 @@ export default class Dissolve extends Vue { | |||||
| <style> | <style> | ||||
| @import url("https://fonts.googleapis.com/css2?family=Coda&display=swap"); | @import url("https://fonts.googleapis.com/css2?family=Coda&display=swap"); | ||||
| * { | |||||
| box-sizing: border-box; | |||||
| } | |||||
| *, | |||||
| *:before, | |||||
| *:after { | |||||
| box-sizing: inherit; | |||||
| } | |||||
| html { | html { | ||||
| width: 100vw; | width: 100vw; | ||||
| height: 100vh; | height: 100vh; | ||||
| @@ -79,25 +88,23 @@ body { | |||||
| background: #111; | background: #111; | ||||
| height: 100%; | height: 100%; | ||||
| width: 100%; | width: 100%; | ||||
| box-sizing: border-box; | |||||
| } | } | ||||
| #app-area { | #app-area { | ||||
| display: flex; | display: flex; | ||||
| flex-direction: row; | flex-direction: row; | ||||
| height: 100%; | |||||
| height: 500px; | |||||
| width: 100%; | width: 100%; | ||||
| } | } | ||||
| #menu { | #menu { | ||||
| flex: 1 0 100px; | |||||
| flex: 1 0; | |||||
| } | } | ||||
| #soundscapes { | #soundscapes { | ||||
| flex: 5 0 50vw; | |||||
| display: grid; | |||||
| grid-template-columns: 400px 400px 400px; | |||||
| grid-auto-rows: auto-fit; | |||||
| flex: 5 0; | |||||
| display: flex; | |||||
| flex-direction: column; | |||||
| } | } | ||||
| .start-button { | .start-button { | ||||
| @@ -30,7 +30,7 @@ import { PresetSources, PresetFilters } from "@/data/presets"; | |||||
| }, | }, | ||||
| }) | }) | ||||
| export default class Menu extends Vue { | export default class Menu extends Vue { | ||||
| presetSources: Array<{ name: string; kind: "Source" }> = PresetSources; | |||||
| presetSources: Array<{ name: string; kind: "source" }> = PresetSources; | |||||
| presetFilters: Array<{ name: string }> = PresetFilters; | presetFilters: Array<{ name: string }> = PresetFilters; | ||||
| } | } | ||||
| </script> | </script> | ||||
| @@ -1,29 +1,35 @@ | |||||
| <template> | <template> | ||||
| <div class="soundscape"> | <div class="soundscape"> | ||||
| <source-node | |||||
| v-for="(source, index) in soundscape.sources" | |||||
| :key="index" | |||||
| :source="source" | |||||
| v-on:delete="deleteSource(source)" | |||||
| > | |||||
| </source-node> | |||||
| <source-node | |||||
| v-on:drop="dropSource" | |||||
| v-on:dragover="dragSource" | |||||
| :dummy="true" | |||||
| ></source-node> | |||||
| <filter-node | |||||
| v-for="(filter, index) in soundscape.filters" | |||||
| :key="index" | |||||
| :filter="filter" | |||||
| v-on:delete="deleteFilter(filter)" | |||||
| > | |||||
| </filter-node> | |||||
| <filter-node | |||||
| v-on:drop="dropFilter" | |||||
| v-on:dragover="dragFilter" | |||||
| :dummy="true" | |||||
| ></filter-node> | |||||
| <div class="soundscape-section"> | |||||
| <source-node | |||||
| v-for="(source, index) in soundscape.sources" | |||||
| :key="index" | |||||
| :source="source" | |||||
| v-on:delete="deleteSource(source)" | |||||
| > | |||||
| </source-node> | |||||
| <source-node | |||||
| v-on:drop="dropSource" | |||||
| v-on:dragenter="dragEnter" | |||||
| v-on:dragover="dragSource" | |||||
| :dummy="true" | |||||
| ></source-node> | |||||
| </div> | |||||
| <div class="soundscape-section"> | |||||
| <filter-node | |||||
| v-for="(filter, index) in soundscape.filters" | |||||
| :key="index" | |||||
| :filter="filter" | |||||
| v-on:delete="deleteFilter(filter)" | |||||
| > | |||||
| </filter-node> | |||||
| <filter-node | |||||
| v-on:drop="dropFilter" | |||||
| v-on:dragenter="dragEnter" | |||||
| v-on:dragover="dragFilter" | |||||
| :dummy="true" | |||||
| ></filter-node> | |||||
| </div> | |||||
| </div> | </div> | ||||
| <div></div> | <div></div> | ||||
| </template> | </template> | ||||
| @@ -51,6 +57,9 @@ export default class SoundscapeComp extends Vue { | |||||
| started = false; | started = false; | ||||
| context!: AudioContext; | context!: AudioContext; | ||||
| dragEnter(event: DragEvent): void { | |||||
| event.preventDefault(); | |||||
| } | |||||
| dragSource(event: DragEvent): void { | dragSource(event: DragEvent): void { | ||||
| if (event.dataTransfer) { | if (event.dataTransfer) { | ||||
| if (event.dataTransfer.types.includes("source")) event.preventDefault(); | if (event.dataTransfer.types.includes("source")) event.preventDefault(); | ||||
| @@ -103,16 +112,19 @@ export default class SoundscapeComp extends Vue { | |||||
| <style scoped> | <style scoped> | ||||
| .soundscape { | .soundscape { | ||||
| width: auto; | |||||
| height: max-content; | height: max-content; | ||||
| margin: auto; | |||||
| padding: 20px; | padding: 20px; | ||||
| margin: 20px; | margin: 20px; | ||||
| display: grid; | |||||
| grid-template-columns: repeat(auto-fit, minmax(1fr, 400px)); | |||||
| grid-auto-rows: auto-fit; | |||||
| grid-gap: 20px; | |||||
| background: #222; | background: #222; | ||||
| border-radius: 30px; | border-radius: 30px; | ||||
| } | } | ||||
| .soundscape-section { | |||||
| display: grid; | |||||
| grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); | |||||
| grid-auto-rows: max-content; | |||||
| grid-gap: 20px; | |||||
| margin-top: 10px; | |||||
| margin-bottom: 10px; | |||||
| } | |||||
| </style> | </style> | ||||
| @@ -1,6 +1,6 @@ | |||||
| export const PresetSources: Array<{ | export const PresetSources: Array<{ | ||||
| name: string; | name: string; | ||||
| kind: "Source"; | |||||
| kind: "source"; | |||||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||||
| [x: string]: any; | [x: string]: any; | ||||
| }> = [ | }> = [ | ||||
| @@ -31,7 +31,7 @@ export const PresetSources: Array<{ | |||||
| "gurgles/gurgle (21)", | "gurgles/gurgle (21)", | ||||
| ], | ], | ||||
| }, | }, | ||||
| kind: "Source", | |||||
| kind: "source", | |||||
| volume: 1, | volume: 1, | ||||
| interval: [4, 6], | interval: [4, 6], | ||||
| pitch: [0.9, 1.1], | pitch: [0.9, 1.1], | ||||
| @@ -44,7 +44,7 @@ export const PresetSources: Array<{ | |||||
| name: "Squishing", | name: "Squishing", | ||||
| soundKeys: ["squishing"], | soundKeys: ["squishing"], | ||||
| }, | }, | ||||
| kind: "Source", | |||||
| kind: "source", | |||||
| volume: 1, | volume: 1, | ||||
| pitch: 1, | pitch: 1, | ||||
| name: "Squishing", | name: "Squishing", | ||||
| @@ -57,7 +57,7 @@ export const PresetFilters: Array<{ name: string; [x: string]: any }> = [ | |||||
| { | { | ||||
| cutoff: 1000, | cutoff: 1000, | ||||
| name: "Lowpass Filter", | name: "Lowpass Filter", | ||||
| kind: "Filter", | |||||
| kind: "filter", | |||||
| type: "LowpassFilter", | type: "LowpassFilter", | ||||
| }, | }, | ||||
| ]; | ]; | ||||
| @@ -1,7 +1,7 @@ | |||||
| import { Node, context } from "../audio"; | import { Node, context } from "../audio"; | ||||
| export abstract class Filter extends Node { | export abstract class Filter extends Node { | ||||
| public abstract kind: string; | |||||
| public kind = "filter"; | |||||
| public input: GainNode; | public input: GainNode; | ||||
| protected filterInput: GainNode; | protected filterInput: GainNode; | ||||
| protected bypass: GainNode; | protected bypass: GainNode; | ||||
| @@ -1,7 +1,6 @@ | |||||
| import { Filter } from "./Filter"; | import { Filter } from "./Filter"; | ||||
| import { context, exposedNumber } from "../audio"; | import { context, exposedNumber } from "../audio"; | ||||
| export class HighpassFilter extends Filter { | export class HighpassFilter extends Filter { | ||||
| public kind = "Biquad Filter"; | |||||
| private biquad: BiquadFilterNode; | private biquad: BiquadFilterNode; | ||||
| @exposedNumber({ | @exposedNumber({ | ||||
| @@ -1,7 +1,6 @@ | |||||
| import { Filter } from "./Filter"; | import { Filter } from "./Filter"; | ||||
| import { context, exposedNumber } from "../audio"; | import { context, exposedNumber } from "../audio"; | ||||
| export class LowpassFilter extends Filter { | export class LowpassFilter extends Filter { | ||||
| public kind = "Biquad Filter"; | |||||
| private biquad: BiquadFilterNode; | private biquad: BiquadFilterNode; | ||||
| @exposedNumber({ | @exposedNumber({ | ||||
| @@ -1,7 +1,6 @@ | |||||
| import { Filter } from "./Filter"; | import { Filter } from "./Filter"; | ||||
| import { context, exposedNumber } from "../audio"; | import { context, exposedNumber } from "../audio"; | ||||
| export class StereoWidthFilter extends Filter { | export class StereoWidthFilter extends Filter { | ||||
| public kind = "Stereo Width"; | |||||
| private mono: GainNode; | private mono: GainNode; | ||||
| private stereo: GainNode; | private stereo: GainNode; | ||||
| @@ -10,8 +10,10 @@ import { | |||||
| } from "./serialize"; | } from "./serialize"; | ||||
| import { IntervalSource } from "./sources/IntervalSource"; | import { IntervalSource } from "./sources/IntervalSource"; | ||||
| import { SoundSet } from "./sources/Source"; | import { SoundSet } from "./sources/Source"; | ||||
| import { polyfill } from "mobile-drag-drop"; | |||||
| createApp(Dissolve).mount("#app"); | createApp(Dissolve).mount("#app"); | ||||
| polyfill(); | |||||
| (window as any).IntervalSource = IntervalSource; | (window as any).IntervalSource = IntervalSource; | ||||
| (window as any).LowpassFilter = LowpassFilter; | (window as any).LowpassFilter = LowpassFilter; | ||||
| @@ -23,7 +23,7 @@ export class SoundSet { | |||||
| } | } | ||||
| export abstract class Source extends Node { | export abstract class Source extends Node { | ||||
| kind = "Source"; | |||||
| kind = "source"; | |||||
| @exposedSoundSet({ | @exposedSoundSet({ | ||||
| name: "Sounds", | name: "Sounds", | ||||