Better export UI
This commit is contained in:
parent
418bca79b2
commit
ff83994733
8 changed files with 195 additions and 109 deletions
|
|
@ -268,9 +268,9 @@ export class StrudelMirror {
|
|||
this.flash();
|
||||
await this.repl.evaluate(this.code);
|
||||
}
|
||||
async exportAudio(begin, end) {
|
||||
async exportAudio(begin, end, sampleRate, downloadName = undefined) {
|
||||
await this.repl.evaluate(this.code, false)
|
||||
await this.repl.exportAudio(begin, end);
|
||||
await this.repl.exportAudio(begin, end, sampleRate, downloadName);
|
||||
this.repl.scheduler.stop();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,14 +112,12 @@ export class Cyclist {
|
|||
this.setStarted(true);
|
||||
}
|
||||
|
||||
async exportAudio(begin, end) {
|
||||
async exportAudio(begin, end, sampleRate, downloadName = undefined) {
|
||||
if (!this.pattern) {
|
||||
throw new Error('Scheduler: no pattern set! call .setPattern first.');
|
||||
}
|
||||
logger('[cyclist] exporting');
|
||||
// this.clock.start();
|
||||
// this.setStarted(true);
|
||||
await renderPatternAudio(this.pattern, this.cps, begin, end)
|
||||
await renderPatternAudio(this.pattern, this.cps, begin, end, sampleRate, downloadName)
|
||||
}
|
||||
|
||||
pause() {
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export function repl({
|
|||
|
||||
const stop = () => scheduler.stop();
|
||||
const start = () => scheduler.start();
|
||||
const exportAudio = async (begin, end) => await scheduler.exportAudio(begin, end);
|
||||
const exportAudio = async (begin, end, sampleRate, downloadName = undefined) => await scheduler.exportAudio(begin, end, sampleRate, downloadName);
|
||||
const pause = () => scheduler.pause();
|
||||
const toggle = () => scheduler.toggle();
|
||||
const setCps = (cps) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import { analysers, resetGlobalEffects } from "./superdough.mjs";
|
||||
|
||||
let audioContext;
|
||||
|
||||
export const setDefaultAudioContext = () => {
|
||||
audioContext = new AudioContext();
|
||||
resetGlobalEffects()
|
||||
return audioContext;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import './feedbackdelay.mjs';
|
|||
import './reverb.mjs';
|
||||
import './vowel.mjs';
|
||||
import { nanFallback, _mod, cycleToSeconds } from './util.mjs';
|
||||
import workletsUrl from './worklets.mjs?audioworklet';
|
||||
import workletsUrl from './worklets.mjs?url';
|
||||
import { createFilter, gainNode, getCompressor, getDistortion, getLfo, getWorklet, effectSend } from './helpers.mjs';
|
||||
import { map } from 'nanostores';
|
||||
import { logger } from './logger.mjs';
|
||||
|
|
@ -332,7 +332,7 @@ export let analysers = {},
|
|||
analysersData = {};
|
||||
|
||||
export function getAnalyserById(id, fftSize = 1024, smoothingTimeConstant = 0.5) {
|
||||
if (!analysers[id]) {
|
||||
if (!analysers[id] || analysers[id].audioContext != getAudioContext()) {
|
||||
// make sure this doesn't happen too often as it piles up garbage
|
||||
const analyserNode = getAudioContext().createAnalyser();
|
||||
analyserNode.fftSize = fftSize;
|
||||
|
|
@ -722,7 +722,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
|||
}
|
||||
|
||||
// analyser
|
||||
if (analyze) {
|
||||
if (analyze && !(ac instanceof OfflineAudioContext)) {
|
||||
const analyserNode = getAnalyserById(analyze, 2 ** (fft + 5));
|
||||
const analyserSend = effectSend(post, analyserNode, 1);
|
||||
audioNodes.push(analyserSend);
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ export const webaudioOutput = (hap, _deadline, hapDuration, cps, t) => {
|
|||
return superdough(hap2value(hap), t, hapDuration, cps, hap.whole?.begin.valueOf());
|
||||
};
|
||||
|
||||
export async function renderPatternAudio(pattern, cps, begin, end) {
|
||||
export async function renderPatternAudio(pattern, cps, begin, end, sampleRate, downloadName = undefined) {
|
||||
const oldAudioCtx = getAudioContext()
|
||||
await oldAudioCtx.close()
|
||||
let audioContext = new OfflineAudioContext(2, (end - begin) / cps * 48000, 48000);
|
||||
let audioContext = new OfflineAudioContext(2, (end - begin) / cps * sampleRate, sampleRate);
|
||||
setAudioContext(audioContext)
|
||||
let context = getAudioContext()
|
||||
setSuperdoughAudioController(new SuperdoughAudioController(context))
|
||||
|
|
@ -63,25 +63,20 @@ export async function renderPatternAudio(pattern, cps, begin, end) {
|
|||
}
|
||||
})
|
||||
|
||||
return context.startRendering().then(async (renderedBuffer) => {
|
||||
return context.startRendering().then((renderedBuffer) => {
|
||||
console.log(renderedBuffer)
|
||||
const wavBuffer = audioBufferToWav(renderedBuffer);
|
||||
const blob = new Blob([wavBuffer], { type: 'audio/wav' });
|
||||
let downloadName = ''
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
// remove leading dash if they want an empty download name
|
||||
if (downloadName == '') {
|
||||
downloadName = `${new Date().toISOString()}.wav`;
|
||||
} else {
|
||||
downloadName = downloadName + `-${new Date().toISOString()}.wav`;
|
||||
}
|
||||
downloadName = downloadName ? `${downloadName}.wav` : `${new Date().toISOString()}.wav`;
|
||||
a.download = `${downloadName}`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}).finally(async () => {
|
||||
setAudioContext(null)
|
||||
setSuperdoughAudioController(null)
|
||||
await initAudio()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue