Region processing
This commit is contained in:
@ -10,6 +10,7 @@
|
||||
import { AudioService } from "./lib/audio/services/AudioService";
|
||||
import { downloadWAV } from "./lib/audio/utils/WAVEncoder";
|
||||
import { loadVolume, saveVolume, loadDuration, saveDuration, loadPitchLockEnabled, savePitchLockEnabled, loadPitchLockFrequency, savePitchLockFrequency } from "./lib/utils/settings";
|
||||
import { cropAudio, cutAudio, processSelection } from "./lib/audio/utils/AudioEdit";
|
||||
import { generateRandomColor } from "./lib/utils/colors";
|
||||
import { getRandomProcessor } from "./lib/audio/processors/registry";
|
||||
import type { AudioProcessor } from "./lib/audio/processors/AudioProcessor";
|
||||
@ -39,6 +40,8 @@
|
||||
let pitchLockFrequency = $state(loadPitchLockFrequency());
|
||||
let pitchLockInput = $state(formatFrequency(loadPitchLockFrequency()));
|
||||
let pitchLockInputValid = $state(true);
|
||||
let selectionStart = $state<number | null>(null);
|
||||
let selectionEnd = $state<number | null>(null);
|
||||
|
||||
const showDuration = $derived(engineType !== 'sample');
|
||||
const showRandomButton = $derived(engineType === 'generative');
|
||||
@ -47,6 +50,8 @@
|
||||
const showMutateButton = $derived(engineType === 'generative' && !isProcessed && currentBuffer);
|
||||
const showPitchLock = $derived(engineType === 'generative');
|
||||
const pitchLock = $derived<PitchLock>({ enabled: pitchLockEnabled, frequency: pitchLockFrequency });
|
||||
const hasSelection = $derived(selectionStart !== null && selectionEnd !== null && currentBuffer !== null);
|
||||
const showEditButtons = $derived(hasSelection);
|
||||
|
||||
$effect(() => {
|
||||
audioService.setVolume(volume);
|
||||
@ -89,13 +94,20 @@
|
||||
onVolumeIncrease: (large) => {
|
||||
volume = Math.min(1, volume + (large ? 0.2 : 0.05));
|
||||
},
|
||||
onEscape: () => showModal && closeModal(),
|
||||
onEscape: () => {
|
||||
if (hasSelection) {
|
||||
clearSelection();
|
||||
} else if (showModal) {
|
||||
closeModal();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function generateRandom() {
|
||||
currentParams = engine.randomParams(pitchLock);
|
||||
waveformColor = generateRandomColor();
|
||||
isProcessed = false;
|
||||
clearSelection();
|
||||
regenerateBuffer();
|
||||
}
|
||||
|
||||
@ -154,9 +166,26 @@
|
||||
async function applyProcessor(processor: AudioProcessor) {
|
||||
if (!currentBuffer) return;
|
||||
|
||||
const leftChannel = currentBuffer.getChannelData(0);
|
||||
const rightChannel = currentBuffer.getChannelData(1);
|
||||
const [processedLeft, processedRight] = await processor.process(leftChannel, rightChannel);
|
||||
let processedLeft: Float32Array;
|
||||
let processedRight: Float32Array;
|
||||
|
||||
if (hasSelection) {
|
||||
const start = Math.min(selectionStart!, selectionEnd!);
|
||||
const end = Math.max(selectionStart!, selectionEnd!);
|
||||
const sampleRate = audioService.getSampleRate();
|
||||
|
||||
[processedLeft, processedRight] = await processSelection(
|
||||
currentBuffer,
|
||||
start,
|
||||
end,
|
||||
processor,
|
||||
sampleRate
|
||||
);
|
||||
} else {
|
||||
const leftChannel = currentBuffer.getChannelData(0);
|
||||
const rightChannel = currentBuffer.getChannelData(1);
|
||||
[processedLeft, processedRight] = await processor.process(leftChannel, rightChannel);
|
||||
}
|
||||
|
||||
currentBuffer = audioService.createAudioBuffer([processedLeft, processedRight]);
|
||||
isProcessed = true;
|
||||
@ -169,6 +198,7 @@
|
||||
currentBuffer = null;
|
||||
currentParams = null;
|
||||
isProcessed = false;
|
||||
clearSelection();
|
||||
|
||||
if (engineType === 'generative') {
|
||||
generateRandom();
|
||||
@ -256,6 +286,44 @@
|
||||
showProcessorPopup = false;
|
||||
}
|
||||
|
||||
function handleSelectionChange(start: number | null, end: number | null) {
|
||||
selectionStart = start;
|
||||
selectionEnd = end;
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
selectionStart = null;
|
||||
selectionEnd = null;
|
||||
}
|
||||
|
||||
function cropSelection() {
|
||||
if (!currentBuffer || selectionStart === null || selectionEnd === null) return;
|
||||
|
||||
const start = Math.min(selectionStart, selectionEnd);
|
||||
const end = Math.max(selectionStart, selectionEnd);
|
||||
|
||||
const sampleRate = audioService.getSampleRate();
|
||||
const [newLeft, newRight] = cropAudio(currentBuffer, start, end, sampleRate);
|
||||
|
||||
currentBuffer = audioService.createAudioBuffer([newLeft, newRight]);
|
||||
clearSelection();
|
||||
audioService.play(currentBuffer);
|
||||
}
|
||||
|
||||
function cutSelection() {
|
||||
if (!currentBuffer || selectionStart === null || selectionEnd === null) return;
|
||||
|
||||
const start = Math.min(selectionStart, selectionEnd);
|
||||
const end = Math.max(selectionStart, selectionEnd);
|
||||
|
||||
const sampleRate = audioService.getSampleRate();
|
||||
const [newLeft, newRight] = cutAudio(currentBuffer, start, end, sampleRate);
|
||||
|
||||
currentBuffer = audioService.createAudioBuffer([newLeft, newRight]);
|
||||
clearSelection();
|
||||
audioService.play(currentBuffer);
|
||||
}
|
||||
|
||||
async function closeModal() {
|
||||
showModal = false;
|
||||
await audioService.initialize();
|
||||
@ -375,6 +443,9 @@
|
||||
buffer={currentBuffer}
|
||||
color={waveformColor}
|
||||
{playbackPosition}
|
||||
{selectionStart}
|
||||
{selectionEnd}
|
||||
onselectionchange={handleSelectionChange}
|
||||
onclick={replaySound}
|
||||
/>
|
||||
{/if}
|
||||
@ -391,6 +462,10 @@
|
||||
{#if showMutateButton}
|
||||
<button onclick={mutate}>Mutate (M)</button>
|
||||
{/if}
|
||||
{#if showEditButtons}
|
||||
<button onclick={cropSelection}>Crop</button>
|
||||
<button onclick={cutSelection}>Cut</button>
|
||||
{/if}
|
||||
{#if currentBuffer}
|
||||
<div
|
||||
class="process-button-container"
|
||||
|
||||
Reference in New Issue
Block a user