Fix pause/play audio muting + tidy share page UX
- Fix SynthesisEngine to not cleanup layers on stop, preventing audio from going silent after pause/resume - Remove "Reimagined as classic game-console music" line - Remove procedural playback meta block - Move "Generate your own" CTA inside player card - Update footer with Birdmania credit and Twitter link 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -126,12 +126,6 @@
|
||||
color: var(--gb-light);
|
||||
}
|
||||
|
||||
/* Transform description */
|
||||
.transform-line {
|
||||
margin: 0 0 calc(var(--spacing-unit) * 3) 0;
|
||||
color: var(--gb-light);
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
button {
|
||||
@@ -272,29 +266,20 @@
|
||||
width: 0%;
|
||||
}
|
||||
|
||||
/* Meta block */
|
||||
.meta-block {
|
||||
/* CTA link (inside card) */
|
||||
.cta-link-inline {
|
||||
margin-top: calc(var(--spacing-unit) * 2);
|
||||
padding-top: calc(var(--spacing-unit) * 2);
|
||||
border-top: 2px solid var(--gb-darkest);
|
||||
font-size: 8px;
|
||||
color: var(--gb-light);
|
||||
line-height: 2.2;
|
||||
}
|
||||
|
||||
/* CTA link */
|
||||
.cta-link {
|
||||
display: block;
|
||||
margin-top: calc(var(--spacing-unit) * 3);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.cta-link a {
|
||||
.cta-link-inline a {
|
||||
display: inline-block;
|
||||
color: var(--gb-lightest);
|
||||
text-decoration: none;
|
||||
font-size: 10px;
|
||||
padding: 12px 16px;
|
||||
padding: 12px 20px;
|
||||
border: var(--pixel-border) solid var(--gb-light);
|
||||
background: var(--gb-dark);
|
||||
box-shadow:
|
||||
@@ -302,7 +287,7 @@
|
||||
inset 4px 4px 0 var(--gb-light);
|
||||
}
|
||||
|
||||
.cta-link a:hover {
|
||||
.cta-link-inline a:hover {
|
||||
background: var(--gb-light);
|
||||
color: var(--gb-darkest);
|
||||
box-shadow:
|
||||
@@ -335,11 +320,6 @@
|
||||
<h2 id="songTitle" class="song-title">Loading...</h2>
|
||||
<div id="songArtist" class="song-artist"></div>
|
||||
|
||||
<!-- Transformation Description -->
|
||||
<p id="transformLine" class="transform-line">
|
||||
Reimagined as classic game-console music
|
||||
</p>
|
||||
|
||||
<!-- Play/Pause Control -->
|
||||
<div class="play-container">
|
||||
<button id="playToggleBtn" class="primary" disabled>
|
||||
@@ -359,20 +339,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Metadata -->
|
||||
<div class="meta-block" id="metaBlock">
|
||||
Procedural playback from MIDI structure.<br />
|
||||
Rendered using the Wario Synthesis Engine.
|
||||
<!-- CTA -->
|
||||
<div class="cta-link-inline">
|
||||
<a id="generateOwn" href="/">Generate your own</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA -->
|
||||
<div class="cta-link">
|
||||
<a id="generateOwn" href="/">Generate your own</a>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="/">WARIO SYNTH</a> — Turn any song into retro game console music
|
||||
Wario's Synthesis Engine — created by <a href="https://x.com/b1rdmania" target="_blank" rel="noopener">Birdmania</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -81,20 +81,26 @@ export class SynthesisEngine {
|
||||
|
||||
start(): void {
|
||||
if (this.isPlaying) return;
|
||||
|
||||
|
||||
this.isPlaying = true;
|
||||
this.startTime = this.audioContext.currentTime;
|
||||
|
||||
|
||||
// Reset event indices
|
||||
for (const role of this.roleAssignments.keys()) {
|
||||
this.nextEventIndex.set(role, 0);
|
||||
}
|
||||
|
||||
|
||||
// Restore layer gains (they may have been faded to 0 on stop)
|
||||
for (const [role, layer] of this.layers) {
|
||||
layer.gainNode.gain.cancelScheduledValues(this.audioContext.currentTime);
|
||||
this.restoreLayerGain(layer.gainNode, role);
|
||||
}
|
||||
|
||||
// Start scheduler to play actual MIDI events
|
||||
this.schedulerIntervalId = window.setInterval(() => {
|
||||
this.scheduleEvents();
|
||||
}, this.config.scheduleInterval);
|
||||
|
||||
|
||||
console.log('Started synthesis with', this.roleAssignments.size, 'roles');
|
||||
}
|
||||
|
||||
@@ -193,38 +199,49 @@ export class SynthesisEngine {
|
||||
};
|
||||
}
|
||||
|
||||
private getLayerGainForRole(role: Role): number {
|
||||
switch (role) {
|
||||
case 'bass': return 0.4;
|
||||
case 'drone': return 0.2;
|
||||
case 'ostinato': return 0.3;
|
||||
case 'texture': return 0.1;
|
||||
case 'accents': return 0.5;
|
||||
case 'melody': return 0.35;
|
||||
default: return 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
private restoreLayerGain(gain: GainNode, role: Role): void {
|
||||
gain.gain.setValueAtTime(this.getLayerGainForRole(role), this.audioContext.currentTime);
|
||||
}
|
||||
|
||||
private configureLayerForRole(gain: GainNode, filter: BiquadFilterNode, role: Role): void {
|
||||
gain.gain.value = this.getLayerGainForRole(role);
|
||||
|
||||
switch (role) {
|
||||
case 'bass':
|
||||
gain.gain.value = 0.4;
|
||||
filter.type = 'lowpass';
|
||||
filter.frequency.value = 200;
|
||||
break;
|
||||
case 'drone':
|
||||
gain.gain.value = 0.2;
|
||||
filter.type = 'bandpass';
|
||||
filter.frequency.value = 400;
|
||||
break;
|
||||
case 'ostinato':
|
||||
gain.gain.value = 0.3;
|
||||
filter.type = 'highpass';
|
||||
filter.frequency.value = 300;
|
||||
break;
|
||||
case 'texture':
|
||||
gain.gain.value = 0.1;
|
||||
filter.type = 'bandpass';
|
||||
filter.frequency.value = 800;
|
||||
break;
|
||||
case 'accents':
|
||||
gain.gain.value = 0.5;
|
||||
filter.type = 'peaking';
|
||||
filter.frequency.value = 1000;
|
||||
break;
|
||||
case 'melody':
|
||||
// Passthrough mode - balanced sound for all notes
|
||||
gain.gain.value = 0.35;
|
||||
filter.type = 'lowpass';
|
||||
filter.frequency.value = 4000; // Brighter sound for full range
|
||||
filter.frequency.value = 4000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -398,14 +415,16 @@ export class SynthesisEngine {
|
||||
private fadeOutAllLayers(): void {
|
||||
const fadeTime = this.config.fadeTime;
|
||||
const when = this.audioContext.currentTime;
|
||||
|
||||
|
||||
for (const layer of this.layers.values()) {
|
||||
// Cancel any scheduled ramps and fade to 0
|
||||
layer.gainNode.gain.cancelScheduledValues(when);
|
||||
layer.gainNode.gain.setValueAtTime(layer.gainNode.gain.value, when);
|
||||
layer.gainNode.gain.linearRampToValueAtTime(0, when + fadeTime);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.cleanupLayers();
|
||||
}, fadeTime * 1000 + 100);
|
||||
|
||||
// Don't cleanup layers - keep them connected for resume
|
||||
// Layers are only cleaned up when setupLayers is called with new data
|
||||
}
|
||||
|
||||
private scheduleChord(role: Role, pitches: number[], duration: number, velocity: number, when: number): void {
|
||||
|
||||
Reference in New Issue
Block a user