Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: dsp #20

Merged
merged 33 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
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
8 changes: 7 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,14 @@ jobs:
with:
node-version: 22.x

- name: Uninstall dev deps with scripts
run: npm remove @vite-pwa/assets-generator --force --ignore-scripts=true

- name: Install dependencies
run: npm install --ignore-scripts --force
run: npm i --force --ignore-scripts=true

- name: Install dev deps with scripts
run: npm i @vite-pwa/assets-generator -D --force

- name: Build
run: bun run build
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,14 @@ jobs:
with:
node-version: 22.x

- name: Uninstall dev deps with scripts
run: npm remove @vite-pwa/assets-generator --force --ignore-scripts=true

- name: Install dependencies
run: npm install --ignore-scripts --force
run: npm i --force --ignore-scripts=true

- name: Install dev deps with scripts
run: npm i @vite-pwa/assets-generator -D --force

- name: Build
run: bun run build
Expand Down
4 changes: 3 additions & 1 deletion admin/client.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import '~/lib/watcher.ts'

import { cleanup, hmr, mount } from 'sigui'
import { Admin } from '~/admin/Admin.tsx'
import { setState, state } from '~/src/state.ts'

export const start = mount('#container', target => {
target.replaceChildren(<Admin />)
target.replaceChildren(<Admin /> as HTMLElement)
return cleanup
})

Expand Down
6 changes: 1 addition & 5 deletions admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@
<title>Vasi - Admin</title>
</head>
<body>
<script type="module">
// KEEP: required for AssemblyScript interop.
globalThis.unmanaged = () => {};
</script>
<div id="container"></div>
<script src="/as-interop.js"></script>
<script src="./client.tsx" type="module"></script>
<script src="../lib/watcher.ts" type="module"></script>
</body>
</html>
8 changes: 4 additions & 4 deletions api/auth/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// deno-lint-ignore-file require-await
import { hash } from 'jsr:@denorg/[email protected]'
import { createCookie, randomHash, timeout } from 'utils'
import { createCookie, timeout } from 'utils'
import { ADMINS } from '~/api/admin/actions.ts'
import { UserLogin, UserRegister, UserSession } from "~/api/auth/types.ts"
import { kv } from '~/api/core/app.ts'
Expand Down Expand Up @@ -60,7 +60,7 @@ export async function getUser(nickOrEmail: string) {
export async function loginUser(ctx: Context, nick: string) {
ctx.log('Login:', nick)

const sessionId = randomHash()
const sessionId = crypto.randomUUID()
const sessionKey = ['session', sessionId]

const now = new Date()
Expand Down Expand Up @@ -90,7 +90,7 @@ export async function loginUser(ctx: Context, nick: string) {
}

async function generateEmailVerificationToken(email: string) {
const token = randomHash()
const token = crypto.randomUUID()
const now = new Date()
const expires = new Date(now)
const expireAfterHours = 3 * 24 // 3 days
Expand Down Expand Up @@ -264,7 +264,7 @@ export async function forgotPassword(ctx: Context, email: string) {
return
}

const token = randomHash()
const token = crypto.randomUUID()
const now = new Date()
const expires = new Date(now)
const expireAfterMinutes = 15
Expand Down
3 changes: 1 addition & 2 deletions api/oauth/routes/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { randomHash } from 'utils'
import { z } from 'zod'
import { kv } from '~/api/core/app.ts'
import { Router } from '~/api/core/router.ts'
Expand Down Expand Up @@ -30,7 +29,7 @@ export function mount(app: Router) {
provider,
})

const oauthStateId = randomHash()
const oauthStateId = crypto.randomUUID()
await kv.set(['oauthState', oauthStateId], state, {
expireIn: 30 * 60 * 1000
})
Expand Down
14 changes: 2 additions & 12 deletions api/oauth/routes/github.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { randomHash } from 'utils'
import { z } from 'zod'
import { getUserByEmail, loginUser } from '~/api/auth/actions.ts'
import { kv } from '~/api/core/app.ts'
Expand Down Expand Up @@ -49,7 +48,7 @@ const OAuthUser = z.union([

const headers = {
'content-type': 'application/json',
'user-agent': 'cfw-oauth-login',
'user-agent': 'oauth-login',
accept: 'application/json',
}

Expand Down Expand Up @@ -104,7 +103,7 @@ export function mount(app: Router) {
}

// create oauth session
const id = randomHash()
const id = crypto.randomUUID()
const now = new Date()
const expires = new Date(now)
expires.setMinutes(expires.getMinutes() + 30)
Expand All @@ -122,15 +121,6 @@ export function mount(app: Router) {
url.searchParams.set('id', id)
const res = ctx.redirect(302, url.href)

// res.headers.set('set-cookie', createCookie(
// 'oauth',
// id,
// expires,
// 'HttpOnly',
// 'Secure',
// 'SameSite=Strict'
// ))

return res
}])
}
3 changes: 3 additions & 0 deletions as/assembly/common/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @ts-ignore
@external('env', 'log')
export declare function log(x: i32): void
11 changes: 11 additions & 0 deletions as/assembly/dsp/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const BUFFER_SIZE = 2048
export const MAX_AUDIOS = 1024
export const MAX_FLOATS = 4096
export const MAX_LISTS = 4096
export const MAX_LITERALS = 4096
export const MAX_OPS = 4096
export const MAX_RMSS = 1024
export const MAX_SCALARS = 4096
export const MAX_SOUNDS = 16
export const MAX_TRACKS = 16
export const MAX_VALUES = 1024
155 changes: 155 additions & 0 deletions as/assembly/dsp/core/antialias-wavetable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { nextPowerOfTwo } from '../../util'
import { ANTIALIAS_WAVETABLE_OVERSAMPLING, WAVETABLE_SIZE } from './constants'
import { fft } from './fft'

class Real {
@inline static saw(real: StaticArray<f32>, i: u32, j: u32): void {
const temp: f32 = -1.0 / f32(i)
real[i] = temp
real[j] = -temp
}

@inline static ramp(real: StaticArray<f32>, i: u32, j: u32): void {
const temp: f32 = -1.0 / f32(i)
real[i] = -temp
real[j] = temp
}

@inline static sqr(real: StaticArray<f32>, i: u32, j: u32): void {
const temp: f32 = i & 0x01 ? 1.0 / f32(i) : 0.0
real[i] = -temp
real[j] = temp
}

static sign: f32 = 1.0
@inline static tri(real: StaticArray<f32>, i: u32, j: u32): void {
const temp: f32 = i & 0x01 ? 1.0 / f32(i * i) * (this.sign = -this.sign) : 0.0
real[i] = temp
real[j] = -temp
}
}

export class AntialiasWavetable {
real: StaticArray<f32>
imag: StaticArray<f32>
freqs: StaticArray<f32>
topFreq: f64
maxHarms: u32
numOfTables: u32
tableLength: u32
tableMask: u32
tableIndex: u32 = 0
stepShift: i32 = 0
sampleRate: u32

saw: StaticArray<StaticArray<f32>>
ramp: StaticArray<StaticArray<f32>>
sqr: StaticArray<StaticArray<f32>>
tri: StaticArray<StaticArray<f32>>

constructor(sampleRate: u32) {
let topFreq: f64 = 10
let maxHarms: u32 = u32(f64(sampleRate) / (3.0 * topFreq) + 0.5)
const tableLength: u32 = nextPowerOfTwo(maxHarms) * 2 * ANTIALIAS_WAVETABLE_OVERSAMPLING
const tableMask: u32 = (tableLength - 1) << 2
const numOfTables: u32 = u32(Math.log2(f64(maxHarms)) + 1)

// logi(tableLength)
const saw = new StaticArray<StaticArray<f32>>(numOfTables)
const ramp = new StaticArray<StaticArray<f32>>(numOfTables)
const sqr = new StaticArray<StaticArray<f32>>(numOfTables)
const tri = new StaticArray<StaticArray<f32>>(numOfTables)
for (let i: u32 = 0; i < numOfTables; i++) {
saw[i] = new StaticArray<f32>(tableLength)
ramp[i] = new StaticArray<f32>(tableLength)
sqr[i] = new StaticArray<f32>(tableLength)
tri[i] = new StaticArray<f32>(tableLength)
}

const freqs = new StaticArray<f32>(numOfTables)
const real = new StaticArray<f32>(tableLength)
const imag = new StaticArray<f32>(tableLength)

this.real = real
this.imag = imag
this.freqs = freqs

this.saw = saw
this.ramp = ramp
this.sqr = sqr
this.tri = tri

this.sampleRate = sampleRate
this.topFreq = topFreq
this.maxHarms = maxHarms
this.numOfTables = numOfTables
this.tableLength = tableLength
this.tableMask = tableMask
this.stepShift = i32(Math.log2(f64(WAVETABLE_SIZE))) - i32(Math.log2(f64(this.tableLength)))

this.makeTables(this.saw, Real.saw)
this.makeTables(this.ramp, Real.ramp)
this.makeTables(this.sqr, Real.sqr)
this.makeTables(this.tri, Real.tri)
}

getTableIndex(hz: f32): u32 {
let tableIndex: u32 = 0
while (
hz >= this.freqs[tableIndex]
&& tableIndex < this.numOfTables - 1
) {
tableIndex = tableIndex + 1
}
return tableIndex
}

makeTables(target: StaticArray<StaticArray<f32>>, fn: (real: StaticArray<f32>, i: u32, j: u32) => void): void {
let topFreq: f64 = this.topFreq
let i: u32 = 0
for (let harms: u32 = this.maxHarms; harms >= 1; harms >>= 1) {
this.defineWaveform(harms, fn)
this.makeWavetable(target[i])
this.freqs[i] = f32(topFreq)
topFreq = topFreq * 2
i = i + 1
}
}

defineWaveform(harms: u32, fn: (real: StaticArray<f32>, i: u32, j: u32) => void): void {
if (harms > (this.tableLength >> 1)) {
harms = (this.tableLength >> 1)
}

this.imag.fill(0)
this.real.fill(0)

Real.sign = 1.0
for (let i: u32 = 1, j: u32 = this.tableLength - 1; i <= harms; i++, j--) {
fn(this.real, i, j)
}
}

writeSaw(i: u32, j: u32): void {
const temp: f32 = -1.0 / f32(i)
this.real[i] = temp
this.real[j] = -temp
}

makeWavetable(wave: StaticArray<f32>): void {
fft(this.tableLength, this.real, this.imag)

// calc normal
let scale: f32
let max: f32 = 0.0
for (let i: u32 = 0; i < this.tableLength; i++) {
let temp: f32 = Mathf.abs(this.imag[i])
if (max < temp) max = temp
}
scale = 1.0 / max * 0.999

for (let idx: u32 = 0; idx < this.tableLength; idx++) {
wave[idx] = this.imag[idx] * scale
}
}
}
54 changes: 54 additions & 0 deletions as/assembly/dsp/core/clock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
@unmanaged
export class Clock {
time: f64 = 0
timeStep: f64 = 0
prevTime: f64 = -1
startTime: f64 = 0
endTime: f64 = Infinity
bpm: f64 = 60
coeff: f64 = 0
barTime: f64 = 0
barTimeStep: f64 = 0
loopStart: f64 = -Infinity
loopEnd: f64 = +Infinity
sampleRate: u32 = 44100
jumpBar: i32 = -1
ringPos: u32 = 0
nextRingPos: u32 = 0

reset(): void {
const c: Clock = this
c.ringPos = 0
c.nextRingPos = 0
c.prevTime = -1
c.time = 0
c.barTime = c.startTime
}
update(): void {
const c: Clock = this

c.coeff = c.bpm / 60 / 4
c.timeStep = 1.0 / c.sampleRate
c.barTimeStep = c.timeStep * c.coeff

let bt: f64

// advance barTime
bt = c.barTime + (
c.prevTime >= 0
? (c.time - c.prevTime) * c.coeff
: 0
)
c.prevTime = c.time

// wrap barTime on clock.endTime
const startTime = Math.max(c.loopStart, c.startTime)
const endTime = Math.min(c.loopEnd, c.endTime)

if (bt >= endTime) {
bt = startTime + (bt % 1.0)
}

c.barTime = bt
}
}
3 changes: 3 additions & 0 deletions as/assembly/dsp/core/constants-internal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @ts-ignore
@inline
export const k2PI: f32 = 6.28318530718
4 changes: 4 additions & 0 deletions as/assembly/dsp/core/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const WAVETABLE_SIZE: u32 = 1 << 13
export const DELAY_MAX_SIZE: u32 = 1 << 16
export const SAMPLE_MAX_SIZE: u32 = 1 << 16
export const ANTIALIAS_WAVETABLE_OVERSAMPLING: u32 = 1
Loading
Loading