wip
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
normalize: ./sounds/*.wav
|
||||
for file in $^; do ffmpeg-normalize $${file} --force --normalization-type peak --target-level -1 --output $${file}; done
|
||||
+38
-9
@@ -18,16 +18,45 @@
|
||||
<body>
|
||||
<main id="app" style="display:none">
|
||||
<h1>Startle Machine</h1>
|
||||
<p>Generates silence occasionally broken up by random sound effects.<br>Click the button below and leave this tab to start the prank.</p>
|
||||
<button class="start" :disabled="started" @click="start">{{ started ? 'You can leave this tab now...' : 'Start the prank' }}</button>
|
||||
<br>
|
||||
<button @click="start">Start</button>
|
||||
<button @click="selectAll">Select all</button>
|
||||
<button @click="deselectAll">Deselect all</button>
|
||||
<br>
|
||||
<div style="display:flex;flex-flow: row wrap;">
|
||||
<div style="width: 33%; padding: .5em;" v-for="sound in sounds">
|
||||
<input v-bind:id="`active-${sound.id}`" type="checkbox" v-model="sound.active">
|
||||
<label v-bind:for="`active-${sound.id}`">{{ sound.name }}</label>
|
||||
</div>
|
||||
<h2>Configuration</h2>
|
||||
<table class="config">
|
||||
<colgroup>
|
||||
<col style="width: 25%">
|
||||
<col>
|
||||
<col style="width: 25%">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<td><label for="minDelay">Minimum delay:</label></td>
|
||||
<td><input id="minDelay" type="range" v-model="minDelay" min="1" max="120" /></td>
|
||||
<td>{{ minDelay }} minutes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="maxDelay">Maximum delay:</label></td>
|
||||
<td><input id="maxDelay" type="range" v-model="maxDelay" min="1" max="120" /></td>
|
||||
<td>{{ maxDelay }} minutes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="volume">Volume:</label></td>
|
||||
<td><input id="volume" type="range" v-model="volume" min="0" max="100" /></td>
|
||||
<td>{{ volume }}%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<button :class="randomPitch ? '' : 'inactive'" @click="randomPitch = !randomPitch">Random pitch: {{ randomPitch ? '✅' : '❌' }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<button :class="hidePrank ? '' : 'inactive'" @click="hidePrank = !hidePrank">Blank page on start: {{ hidePrank ? '✅' : '❌' }}</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3>Sounds <small><a href="#" @click="selectAll">Select all</a> - <a href="#" @click="unselectAll">Unselect all</a></small></h3>
|
||||
<div class="sound-container">
|
||||
<button v-for="sound in sounds" :class="sound.active ? '' : 'inactive'" @click="sound.active = !sound.active">{{ sound.name }}</button>
|
||||
</div>
|
||||
<br>
|
||||
<small><a href="https://github.com/klemek" target="_blank">@klemek</a> - <a href="https://github.com/klemek/startle-machine" target="_blank">Github Repository</a> - 2024</small>
|
||||
|
||||
@@ -47,19 +47,32 @@ let app = {
|
||||
data() {
|
||||
return {
|
||||
sounds: [],
|
||||
started: false,
|
||||
minDelay: 15,
|
||||
maxDelay: 45,
|
||||
volume: 50,
|
||||
randomPitch: true,
|
||||
hidePrank: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentYear() {
|
||||
return new Date().getFullYear();
|
||||
computed: {},
|
||||
watch: {
|
||||
minDelay() {
|
||||
this.maxDelay = Math.max(this.minDelay, this.maxDelay);
|
||||
},
|
||||
maxDelay() {
|
||||
this.minDelay = Math.min(this.minDelay, this.maxDelay);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
showApp() {
|
||||
document.getElementById("app").setAttribute("style", "");
|
||||
},
|
||||
hideApp() {
|
||||
document.getElementById("app").setAttribute("style", "display:none");
|
||||
document.body.setAttribute("style", "background: none");
|
||||
},
|
||||
onBlur() {
|
||||
// TODO document.getElementById("app").setAttribute("style", "display:none");
|
||||
// TODO
|
||||
},
|
||||
onFocus() {
|
||||
@@ -74,24 +87,30 @@ let app = {
|
||||
});
|
||||
},
|
||||
start() {
|
||||
const audioCtx = new AudioContext();
|
||||
this.sounds.forEach(sound => {
|
||||
const source = audioCtx.createMediaElementSource(sound.audio);
|
||||
source.connect(audioCtx.destination);
|
||||
});
|
||||
this.trigger();
|
||||
if (!this.started) {
|
||||
const audioCtx = new AudioContext();
|
||||
this.sounds.forEach(sound => {
|
||||
const source = audioCtx.createMediaElementSource(sound.audio);
|
||||
source.connect(audioCtx.destination);
|
||||
});
|
||||
this.trigger();
|
||||
if (this.hidePrank) {
|
||||
this.hideApp();
|
||||
}
|
||||
}
|
||||
},
|
||||
trigger() {
|
||||
const time = random.int(30, 300);
|
||||
console.log(`Next in ${time} seconds`);
|
||||
setTimeout(this.playRandomSound, time * 1000);
|
||||
this.started = true;
|
||||
const time = random.int(this.minDelay, this.maxDelay);
|
||||
console.log(`Next in ${time} minutes`);
|
||||
setTimeout(this.playRandomSound, time * 60 * 1000);
|
||||
},
|
||||
selectAll() {
|
||||
this.sounds.forEach(sound => {
|
||||
sound.active = true;
|
||||
});
|
||||
},
|
||||
deselectAll() {
|
||||
unselectAll() {
|
||||
this.sounds.forEach(sound => {
|
||||
sound.active = false;
|
||||
});
|
||||
@@ -100,7 +119,8 @@ let app = {
|
||||
const sound = random.element(this.sounds.filter(sound => sound.active));
|
||||
if (sound !== null) {
|
||||
console.log(`Playing ${sound.id}`);
|
||||
sound.audio.playbackRate = random.float(0.8, 1.2);
|
||||
sound.audio.playbackRate = this.randomPitch ? random.float(0.8, 1.2) : 1;
|
||||
sound.audio.volume = this.volume / 100;
|
||||
sound.audio.play();
|
||||
}
|
||||
this.trigger();
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -85,8 +85,8 @@ CUSTOM STYLE
|
||||
|
||||
:root {
|
||||
/* https://materialui.co/colors/ */
|
||||
--hue-primary: 65.52;
|
||||
--sat-primary: 20%;
|
||||
--hue-primary: 348.36;
|
||||
--sat-primary: 100.00%;
|
||||
--background: hsl(var(--hue-primary), var(--sat-primary), 96.08%);
|
||||
--background-primary: hsl(var(--hue-primary), var(--sat-primary), 93.33%);
|
||||
--background-secondary: hsl(var(--hue-primary), var(--sat-primary), 90%);
|
||||
@@ -169,3 +169,90 @@ a {
|
||||
font-size: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
button.red {
|
||||
--hue-primary: 4.11;
|
||||
--sat-primary: 89.62%;
|
||||
}
|
||||
|
||||
button.green {
|
||||
--hue-primary: 87.77;
|
||||
--sat-primary: 50.21%;
|
||||
}
|
||||
|
||||
button.inactive {
|
||||
--sat-primary: 25%;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
--sat-primary: 5%;
|
||||
cursor: no-drop;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 1px solid hsl(var(--hue-primary), var(--sat-primary), 50%);
|
||||
background-color: hsl(var(--hue-primary), var(--sat-primary), 90%);
|
||||
border-radius: 0.5em;
|
||||
cursor: pointer;
|
||||
padding: .25em .5em;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: hsl(var(--hue-primary), var(--sat-primary), 92%);
|
||||
}
|
||||
|
||||
button:active {
|
||||
background-color: hsl(var(--hue-primary), var(--sat-primary), 94%);
|
||||
}
|
||||
|
||||
.sound-container {
|
||||
display:flex;
|
||||
flex-flow: row wrap;
|
||||
gap: .5em;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sound-container button {
|
||||
flex-basis: 31%;
|
||||
height: 4em;
|
||||
width: 100%;
|
||||
position:relative;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
.sound-container button::before {
|
||||
content: '🔊';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: .25em;
|
||||
left: .25em;
|
||||
}
|
||||
|
||||
.sound-container button.inactive::before {
|
||||
content: '🔇';
|
||||
}
|
||||
|
||||
h3 > a {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.start {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
font-size: larger;
|
||||
padding: .5em;
|
||||
}
|
||||
|
||||
table.config td:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table.config input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.config button {
|
||||
width: 100%;
|
||||
margin-top: .5em;
|
||||
}
|
||||
Reference in New Issue
Block a user