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
25 changes: 17 additions & 8 deletions app/instrument/[id]/components/audio-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import {
} from "@/components/ui/select";
import { useFmSynthesizer } from "@/contexts/synth";
import type { FmInstrument } from "@/features/instrument/types";
import { AVAILABLE_CHIPS } from "@/features/preview/consts";
import { AVAILABLE_CHIP_MAP } from "@/features/preview/consts";
import { clampOctave, withinOctave } from "@/features/preview/octave";
import { type Pitch, pitchToString } from "@/features/preview/pitch";
import type { AvailableChip } from "@/features/preview/types";
import { useSynthSettings } from "@/stores/synth-settings";
import { iota } from "@/utils/range";

const DISPLAYED_OCTAVE_RANGE = 4;
Expand All @@ -42,7 +43,7 @@ export function AudioPreview({
}: {
instrument: FmInstrument;
}): React.JSX.Element {
const [playbackChip, setPlaybackChip] = useState<AvailableChip>("OPNA");
const { playbackChip, setPlaybackChip } = useSynthSettings();
const [octaveOffset, setOctaveOffset] = useState<number>(3);

// Key: pointerId, Value: ActiveNote
Expand All @@ -66,6 +67,12 @@ export function AudioPreview({
};
}, [synthContext]);

function handlePlaybackChipValueChanged(value: string) {
const chipType = value as AvailableChip;
synthContext?.changeChip(chipType);
setPlaybackChip(chipType);
}

async function noteOn(pitch: Pitch, pointerId: number) {
await synthContext?.keyOn(pitch, pointerId);
}
Expand Down Expand Up @@ -161,17 +168,19 @@ export function AudioPreview({
<Label className="text-sm font-medium">Chip:</Label>
<Select
value={playbackChip}
onValueChange={(chip) => setPlaybackChip(chip as AvailableChip)}
onValueChange={handlePlaybackChipValueChanged}
>
<SelectTrigger className="w-32">
<SelectValue />
</SelectTrigger>
<SelectContent>
{AVAILABLE_CHIPS.map((chip) => (
<SelectItem key={chip} value={chip}>
{chip}
</SelectItem>
))}
{[
...AVAILABLE_CHIP_MAP.keys().map((chip) => (
<SelectItem key={chip} value={chip}>
{chip}
</SelectItem>
)),
]}
</SelectContent>
</Select>
</div>
Expand Down
12 changes: 11 additions & 1 deletion features/preview/consts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import type { AvailableChip } from "./types";

export const AVAILABLE_CHIPS: AvailableChip[] = ["OPNA"] as const;
/**
* Map of available sound chips and their corresponding values.
* The values must match the ones defined in WASM.
*/
export const AVAILABLE_CHIP_MAP: ReadonlyMap<AvailableChip, number> = new Map<
AvailableChip,
number
>([
["OPNA", 0],
["OPN2", 1],
]);
20 changes: 20 additions & 0 deletions features/preview/synth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useEffect, useRef } from "react";
import type { FmInstrument } from "../instrument/types";
import { AVAILABLE_CHIP_MAP } from "./consts";
import type { Pitch } from "./pitch";
import type { AvailableChip } from "./types";

export type Synthesizer = {
audioContext: AudioContext;
Expand All @@ -12,6 +14,7 @@ export type Synthesizer = {
keyOff: (id: number) => void;
setInstrument: (instrument: FmInstrument) => void;
reset: () => void;
changeChip: (chip: AvailableChip) => void;
};

const RING_BUFFER_SIZE = 8192;
Expand Down Expand Up @@ -44,6 +47,11 @@ type ResetWorkerRequestMessage = {
type: "reset";
};

type ChangeChipWorkerRequestMessage = {
type: "changeChip";
chipId: number;
};

type LoadWasmWorkerRequestMessage = {
type: "loadWasm";
loaderUrl: string;
Expand Down Expand Up @@ -234,6 +242,18 @@ export function useSynthesizer() {
type: "reset",
} satisfies ResetWorkerRequestMessage);
},

changeChip: (chip) => {
const chipId = AVAILABLE_CHIP_MAP.get(chip);
if (chipId === undefined) {
return;
}

wasmWorker.postMessage({
type: "changeChip",
chipId,
} satisfies ChangeChipWorkerRequestMessage);
},
};
})();

Expand Down
2 changes: 1 addition & 1 deletion features/preview/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import type { RecommendedChip } from "../instrument/models";

type Subset<T, U extends T> = U;

export type AvailableChip = Subset<RecommendedChip, "OPNA">;
export type AvailableChip = Subset<RecommendedChip, "OPNA" | "OPN2">;
Binary file modified public/wasm/synth.wasm
Binary file not shown.
21 changes: 21 additions & 0 deletions public/worker/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ importScripts("/wasm/synth.js");

let wasm;

let currentInstrument;

/** @type {Int32Array} */
let control;
const SAB_CONTROL_SIZE = 2;
Expand Down Expand Up @@ -127,6 +129,7 @@ function handleSetInstrument(instrument) {
}

wasm.setInstrument(instrument);
currentInstrument = instrument;
}

/**
Expand Down Expand Up @@ -197,6 +200,20 @@ function handleGenerate() {
Atomics.store(control, SAB_WRITE_POSITION_INDEX, pos);
}

function handleChangeChip(id) {
if (!wasm) {
self.postMessage(WASM_NOT_LOADED_MESSAGE);
return;
}

wasm.changeChip(id);
messageQueue.length = 0;

if (currentInstrument) {
wasm.setInstrument(currentInstrument);
}
}

self.onmessage = async ({ data }) => {
switch (data.type) {
case "loadWasm":
Expand All @@ -217,6 +234,10 @@ self.onmessage = async ({ data }) => {
handleReset();
break;

case "changeChip":
handleChangeChip(data.chipId);
break;

default:
break;
}
Expand Down
4 changes: 2 additions & 2 deletions stores/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { create } from "zustand";

export type ViewMode = "grid" | "list";

interface SearchStore {
type SearchStore = {
viewMode: ViewMode;
setViewMode: (mode: ViewMode) => void;
}
};

export const useSearchStore = create<SearchStore>((set) => ({
viewMode: "grid",
Expand Down
12 changes: 12 additions & 0 deletions stores/synth-settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { create } from "zustand";
import type { AvailableChip } from "@/features/preview/types";

type SynthSettings = {
playbackChip: AvailableChip;
setPlaybackChip: (chip: AvailableChip) => void;
};

export const useSynthSettings = create<SynthSettings>((set) => ({
playbackChip: "OPNA",
setPlaybackChip: (chip) => set({ playbackChip: chip }),
}));
Loading