This commit is contained in:
Klemek
2024-10-18 16:01:49 +02:00
parent 866e2aaa28
commit 3dc6034ae0
30 changed files with 164 additions and 26 deletions
+2
View File
@@ -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
View File
@@ -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">&nbsp;
<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>
+29 -9
View File
@@ -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() {
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.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
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.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+89 -2
View File
@@ -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;
}