Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ public/
.claude/
*.tsbuildinfo
.vercel
dist
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ npx vercel blob put \
--content-type audio/mpeg
```

If you need to test or debug the production configuration locally, you can run `npx vercel dev`. This will run the Vercel CLI's dev server and use the function defined in `/api/samples/list.tsx`, which will refer to the samples stored in Vercel Blob.

## Contributing

Contributions are welcome! Please open an issue to get feedback on your idea before making a PR.
Expand Down
6 changes: 5 additions & 1 deletion src/ui/components/SamplesSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useAtom } from 'jotai'
import { ChevronDown, Music } from 'lucide-react'
import type * as Tone from 'tone'

import { PLUGINS } from '@/src/lang/plugins'

import type { SampleDetails } from '../load_samples'
import { editorLanguageAtom } from '../state'
import type { EditorLanguage } from './Editor'
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from './shadcn-ui/collapsible'
import { useToast } from './shadcn-ui/hooks/use-toast'
Expand Down Expand Up @@ -38,6 +40,8 @@ export function SamplesSidebar({
onLanguageChange: (value: EditorLanguage) => void
}) {
const { toast } = useToast()
const [editorLanguage] = useAtom(editorLanguageAtom)

return (
<Sidebar variant='floating'>
<SidebarHeader>
Expand Down Expand Up @@ -68,7 +72,7 @@ export function SamplesSidebar({
<SidebarGroupContent>
<SidebarMenuItem>
<Select
defaultValue='typescript' // TODO: use the default from the atom instead
value={editorLanguage}
onValueChange={(value: EditorLanguage) => {
onLanguageChange(value)
}}
Expand Down
158 changes: 111 additions & 47 deletions src/ui/default_python.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,123 @@
import json

data = {
"bpm": 70,
"instruments": [
# Welcome to 'Minuet', a multi-language live coding platform for the web.
# You are now coding music in Python in the browser!
#
# Press Ctrl-Enter to start playback. Then modify the code as you like,
# and then press Ctrl-Enter again to update the phrase-plan.
#
# Refresh the page or close the tab if you want to exit.
#
# Minuet will maintain a 4-bar loop. You can describe durations and
# note placement according to the Tone.js Time definitions:
# https://tonejs.github.io/docs/15.1.22/types/Unit.Time.html
# For example, use "0:0:0" for the downbeat of the first bar,
# and "1:2:0" for the *third* beat of the *second* bar - don't
# forget about zero-indexing! The "bars:beats:sixteenths" syntax
# can be used to describe both placements and durations.
# You can also describe durations using syntax like
# "8n" for an eighth note, "4n" for a quarter note, etc.
#
# You can find the source code or file an issue on GitHub:
# https://github.com/jasongwartz/minuet

bd = {
"sample": {"name": "kick.mp3"},
"on": [
f"{start + bar}:{beat}:{sub}"
for start in [0, 2]
for (bar, beat, sub) in [
(0, 0, 0),
(0, 1, 2),
(0, 3, 0),
(1, 1, 0),
(1, 2, 2),
]
],
}

sd = {
"sample": {"name": "snare.mp3"},
"on": [
f.format(i)
for i in range(0, 4)
for f in [
"{}:1:0",
"{}:3:0",
]
],
}

hh = {
"sample": {
"name": "hihat_loop.mp3",
"stretchTo": "2:0:0",
},
"on": ["0:0:0", "2:0:0"],
}

lead_synth = {
"synth": "AMSynth",
"on": [
{
"sample": {
"name": "7_Drum_01_85bpm.wav",
"stretchTo": "4:0:0"
},
"on": [0],
"with": []
},
"notes": ["A4", "B4", "C5", "D5", "E5", "G5"],
"mode": "loop",
"beat": 0,
"duration": "16n",
"every": "16n",
"order": "random",
"octaveVariance": 2,
}
],
"with": [
{
"sample": {
"name": "Cymatics - Lofi Kick 5 - D#.wav"
"name": "hpf",
"value": {
"from": {
"oscillator": "lfo",
"period": "3:0:0",
"min": "C4",
"max": "C7",
}
},
"on": [0, "1:0:0", "2:0:0", "3:0:0"],
"with": []
}
],
}

bass_synth = {
"synth": "AMSynth",
"on": [
{
"notes": ["A1"],
"beat": 0,
"duration": "2:0:0",
"mode": "once",
},
{
"sample": {
"name": "Cymatics - Lofi Snare 6 - F#.wav"
},
"on": ["0:1:0", "0:3:0", "1:1:0", "1:3:0", "2:1:0", "2:3:0", "3:1:0", "3:3:0"],
"with": []
"notes": ["F1"],
"beat": "2:0:0",
"duration": "1:2:0",
"mode": "once",
},
{
"synth": "FMSynth",
"on": [
{"notes": ["C1"], "beat": 0, "duration": "0:3:0", "mode": "once"},
{"notes": ["G1"], "beat": "0:3:0", "duration": "4n", "mode": "once"},
{"notes": ["F1"], "beat": "1:0:0", "duration": "1:0:0", "mode": "once"},
{"notes": ["Bb1"], "beat": "1:3:0", "duration": "8n", "mode": "once"},
{"notes": ["B1", "C2"], "beat": "1:3:2", "duration": "16n", "mode": "once"},
{"notes": ["C1"], "beat": "2:0:0", "duration": "0:3:0", "mode": "once"},
{"notes": ["G1"], "beat": "2:3:0", "duration": "4n", "mode": "once"},
{"notes": ["F1"], "beat": "3:0:0", "duration": "1:0:0", "mode": "once"},
{"notes": ["Bb1"], "beat": "3:3:0", "duration": "8n", "mode": "once"},
{"notes": ["B1", "C2"], "beat": "3:3:2", "duration": "16n", "mode": "once"}
],
"with": [
{
"name": "lpf",
"value": {
"from": {
"oscillator": "lfo",
"min": "C2",
"max": "C5",
"period": "0:0:3"
}
}
}
]
}
]
"notes": ["D1"],
"beat": "3:2:0",
"duration": "0:2:0",
"mode": "once",
},
],
}


data = {
"bpm": 105,
"instruments": [
bd,
sd,
hh,
lead_synth,
bass_synth,
],
}

json.dumps(data)
94 changes: 43 additions & 51 deletions src/ui/default_ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,64 +12,56 @@ const hhOff16s = Array(16)
instruments: [
{
sample: {
name: 'Cymatics - Lofi Kick 5 - D#.wav',
name: 'kick.mp3',
},
on: bdQuarters,
},
{
sample: {
name: 'Cymatics - Lofi Snare 6 - F#.wav',
name: 'hihat_loop.mp3',
},
on: hhOff16s,
// on: ['0:1:0', '0:3:0', '1:1:0', '1:3:0', '2:1:0', '2:3:0', '3:1:0', '3:3:0'],
},
// {
// synth: "AMSynth",
// on: [{ notes: ["C3", "Eb3", "F3", "Bb3"], beat: 0, duration: "32n", every: "32n", mode: "loop", octaveVariance: 0 }],
// with: []
// },
// {
// synth: {
// output: 3,
// loopback: {
// input: 'Minifuse 2',
// channel: 'left',
// },
// },
// on: [
// { notes: ['C1'], beat: 0, duration: '0:3:0', mode: 'once' },
// { notes: ['G1'], beat: '0:3:0', duration: '4n', mode: 'once' },
// { notes: ['F1'], beat: '1:0:0', duration: '1:0:0', mode: 'once' },
// { notes: ['Bb1'], beat: '1:3:0', duration: '8n', mode: 'once' },
// { notes: ['B1', 'C2'], beat: '1:3:2', duration: '16n', mode: 'once' },
// { notes: ['C1'], beat: '2:0:0', duration: '0:3:0', mode: 'once' },
// { notes: ['G1'], beat: '2:3:0', duration: '4n', mode: 'once' },
// { notes: ['F1'], beat: '3:0:0', duration: '1:0:0', mode: 'once' },
// { notes: ['Bb1'], beat: '3:3:0', duration: '8n', mode: 'once' },
// { notes: ['B1', 'C2'], beat: '3:3:2', duration: '16n', mode: 'once' },

// // { notes: ["C1", "Eb1", "F1", "Bb1"], beat: 0, duration: "32n", every: "16n", mode: "loop", octaveVariance: 0 }
// ],
// with: [
// {
// name: 'lpf',
// value: {
// from: {
// oscillator: 'lfo',
// min: 'C2',
// max: 'C5',
// period: '0:0:3',
// },
// },
// },
// ],
// },
// {
// external: {
// input: "Minifuse 2",
// channel: "left"
// },
// with: []
// }
{
synth: 'AMSynth',
on: [
{
notes: ['C3', 'Eb3', 'F3', 'Bb3'],
beat: 0,
duration: '32n',
every: '32n',
mode: 'loop',
octaveVariance: 1,
},
],
},
{
synth: 'AMSynth',
on: [
{ notes: ['C1'], beat: 0, duration: '0:3:0', mode: 'once' },
{ notes: ['G1'], beat: '0:3:0', duration: '4n', mode: 'once' },
{ notes: ['F1'], beat: '1:0:0', duration: '1:0:0', mode: 'once' },
{ notes: ['Bb1'], beat: '1:3:0', duration: '8n', mode: 'once' },
{ notes: ['B1', 'C2'], beat: '1:3:2', duration: '16n', mode: 'once' },
{ notes: ['C1'], beat: '2:0:0', duration: '0:3:0', mode: 'once' },
{ notes: ['G1'], beat: '2:3:0', duration: '4n', mode: 'once' },
{ notes: ['F1'], beat: '3:0:0', duration: '1:0:0', mode: 'once' },
{ notes: ['Bb1'], beat: '3:3:0', duration: '8n', mode: 'once' },
{ notes: ['B1', 'C2'], beat: '3:3:2', duration: '16n', mode: 'once' },
],
with: [
{
name: 'lpf',
value: {
from: {
oscillator: 'lfo',
min: 'C2',
max: 'C5',
period: '0:0:3',
},
},
},
],
},
],
})
37 changes: 8 additions & 29 deletions src/ui/default_yaml.yaml
Original file line number Diff line number Diff line change
@@ -1,33 +1,12 @@
bpm: 70
bpm: 105
instruments:
- sample:
name: Cymatics - Lofi Kick 5 - D#.wav
on: ['0:0:0', '1:0:0', '2:0:0', '3:0:0']

name: kick.mp3
on: ['0:0:0', '0:1:2', '0:3:0', '1:1:0', '1:2:2', '2:0:0', '2:1:2', '2:3:0', '3:1:0', '3:2:2']
- sample:
name: bbc_weather---_07038337.mp3
with: []
on: loop

name: snare.mp3
on: ['0:1:0', '0:3:0', '1:1:0', '1:3:0', '2:1:0', '2:3:0', '3:1:0', '3:3:0']
- sample:
name: Cymatics - Lofi Hihat Loop 15 - 85 BPM.wav
stretchTo: '4:0:0'
on: [0]
- sample:
name: ellington trane in a sentimental mood intro.mp3
stretchTo: '2:0:0'
on: [0, '2:0:0']
- synth: FMSynth
on:
- notes: ['B2']
beat: 0
duration: '2:0:0'
mode: once
- notes: ['G1']
beat: '2:0:0'
duration: '1:2:0'
mode: once
- notes: ['E1']
beat: '3:2:0'
duration: '0:2:0'
mode: once
name: hihat_loop.mp3
stretchTo: 2:0:0
on: ['0:0:0', '2:0:0']
2 changes: 1 addition & 1 deletion src/ui/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ export const schedulingStatusIndicatorAtom = atom<{
text: '...',
})

export const editorLanguageAtom = atom<EditorLanguage>('typescript')
export const editorLanguageAtom = atom<EditorLanguage>('python')