Skip to content

Commit 5416240

Browse files
committed
Chord-progression enhancements
1 parent 3163eb5 commit 5416240

File tree

11 files changed

+413
-67
lines changed

11 files changed

+413
-67
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Music Mate
22

3-
Live: https://danrose499.github.io/Music-Mate/
3+
Live: https://danielrosenthal.dev/Music-Mate/
44

55
![App Page](src/assets/images/Screenshot.png?raw=true "App Page")
66

package-lock.json

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@sveltejs/vite-plugin-svelte": "^3.0.0",
1717
"autoprefixer": "^10.4.18",
1818
"postcss": "^8.4.35",
19+
"svelte-preprocess": "^6.0.3",
1920
"tailwindcss": "^3.4.10",
2021
"vite": "^5.2.0",
2122
"vite-plugin-pwa": "^0.20.0"

postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}

src/App.svelte

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
let tremolo
2828
let compressor
2929
let started = false
30-
let loop = false
30+
let loop = true
3131
let part
3232
let isTouch = false
33+
let currentBeat = -1
3334
3435
// Clicking a chord selects it (updates diagrams) without triggering audio
3536
function selectChord(chord) {
@@ -141,43 +142,72 @@
141142
progression = [...progression.slice(0, i), item, ...progression.slice(i)]
142143
}
143144
144-
async function playProgression() {
145-
await ensureAudio()
146-
Tone.Transport.stop()
145+
// Reactive statement to update the sequence whenever the progression changes
146+
$: if (progression && started) {
147+
if (Tone.Transport.state === 'started') {
148+
updateSequence(true) // restart playback
149+
}
150+
}
151+
152+
// This function builds or rebuilds the Tone.js Part from the progression
153+
function updateSequence(restart = false) {
154+
const wasRunning = Tone.Transport.state === 'started'
155+
if (wasRunning) Tone.Transport.stop()
156+
147157
Tone.Transport.cancel(0)
148-
if (part) { part.dispose(); part = null }
158+
if (part) {
159+
part.dispose()
160+
part = null
161+
}
162+
163+
if (progression.length === 0) {
164+
currentBeat = -1
165+
return
166+
}
149167
150168
const quarter = Tone.Time('4n').toSeconds()
151169
const events = []
152170
let acc = 0
153-
for (let i = 0; i < progression.length; i++) {
154-
const item = progression[i]
171+
progression.forEach((item, i) => {
155172
const notes = item?.chord ? chordNotes(item.chord) : []
156-
const strums = (item?.beats || 4)
157-
for (let s = 0; s < strums; s++) {
173+
const beats = item?.beats || 4
174+
item.startBeat = Math.floor(acc / quarter)
175+
for (let s = 0; s < beats; s++) {
158176
const dir = 'down'
159-
events.push({ time: acc + s * quarter, notes, dir })
177+
events.push({ time: acc + s * quarter, notes, dir, beat: item.startBeat + s, chord: item.chord })
160178
}
161-
acc += (item?.beats || 4) * quarter
162-
}
163-
164-
if (events.length === 0) return
179+
acc += beats * quarter
180+
})
165181
166182
part = new Tone.Part((time, ev) => {
183+
currentBeat = ev.beat
184+
if (currentChord !== ev.chord) {
185+
currentChord = ev.chord
186+
}
167187
if (ev.notes && ev.notes.length) {
168188
strumChord(ev.notes, time, ev.dir)
169189
}
170190
}, events).start(0)
171191
part.loop = loop
172192
part.loopEnd = acc
173193
174-
Tone.Transport.start('+0.05')
194+
if (restart || wasRunning) {
195+
Tone.Transport.start('+0.05')
196+
}
197+
}
198+
199+
async function playProgression() {
200+
await ensureAudio()
201+
currentBeat = 0
202+
updateSequence(true)
203+
Tone.Transport.on('stop', () => { currentBeat = -1 })
175204
}
176205
177206
function stopProgression() {
178207
Tone.Transport.stop()
179208
Tone.Transport.cancel(0)
180209
if (part) { part.dispose(); part = null }
210+
currentBeat = -1
181211
}
182212
</script>
183213
@@ -205,7 +235,7 @@
205235
<ChordButton name={c} {isTouch} onPreview={() => preview(c)} onAdd={() => addToProgression(c)} onSelect={() => selectChord(c)} />
206236
{/each}
207237
<!-- Rest tile (draggable) -->
208-
<ChordButton name="Rest" {isTouch} dragPayload="REST" onAdd={addRestToProgression} />
238+
<ChordButton name="Rest" {isTouch} dragPayload="REST" onAdd={addRestToProgression} onSelect={() => selectChord('Rest')} />
209239
</div>
210240
</div>
211241
@@ -220,11 +250,11 @@
220250
<div class="grid grid-cols-2 gap-3">
221251
<div class="space-y-2">
222252
<div class="text-xs uppercase tracking-wide text-slate-400">Guitar</div>
223-
<ChordDiagram instrument="guitar" shape={guitarShapes[currentChord]} />
253+
<ChordDiagram instrument="guitar" shape={{...guitarShapes[currentChord || 'Rest'], chord: currentChord || 'Rest'}} clickable={true} on:click={() => preview(currentChord)} />
224254
</div>
225255
<div class="space-y-2">
226256
<div class="text-xs uppercase tracking-wide text-slate-400">Ukulele</div>
227-
<ChordDiagram instrument="uke" shape={ukeShapes[currentChord]} />
257+
<ChordDiagram instrument="uke" shape={{...ukeShapes[currentChord || 'Rest'], chord: currentChord || 'Rest'}} clickable={true} on:click={() => preview(currentChord)} />
228258
</div>
229259
</div>
230260
{/if}
@@ -233,7 +263,7 @@
233263
234264
<section class="space-y-4">
235265
<h2 class="font-semibold text-slate-200">Progression</h2>
236-
<ProgressionBar {progression} {isTouch} onRemove={removeFromProgression} onUpdateBeat={updateBeats} onReorder={reorderProgression} onInsert={insertAt} />
266+
<ProgressionBar {progression} {currentBeat} {isTouch} onRemove={removeFromProgression} onUpdateBeat={updateBeats} onReorder={reorderProgression} onInsert={insertAt} />
237267
<div class="flex flex-wrap items-center gap-3">
238268
<button class="px-4 py-2 rounded-lg bg-cyan-500 hover:bg-cyan-400 text-slate-900 font-semibold"
239269
on:click={playProgression}

src/components/ChordButton.svelte

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,18 @@
5757
▶︎
5858
</button>
5959
60+
<button type="button" class="px-2 py-1 rounded bg-cyan-500 hover:bg-cyan-400 text-slate-900 text-sm"
61+
on:click|stopPropagation={() => onAdd?.()}
62+
on:touchstart|stopPropagation|preventDefault={() => onAdd?.()}>
63+
Add
64+
</button>
6065
<style>
6166
/* Hide drag handle on mobile/tablet (coarse pointer) in all orientations */
6267
@media (pointer: coarse) {
6368
.drag-icon { display: none !important; }
6469
}
6570
/* Prevent long-press image drag ghosting on iOS */
6671
img[draggable="false"] { -webkit-user-drag: none; }
67-
}</style>
68-
<button type="button" class="px-2 py-1 rounded bg-cyan-500 hover:bg-cyan-400 text-slate-900 text-sm"
69-
on:click|stopPropagation={() => onAdd?.()}
70-
on:touchstart|stopPropagation|preventDefault={() => onAdd?.()}>
71-
Add
72-
</button>
72+
</style>
7373
</div>
7474
</button>

0 commit comments

Comments
 (0)