Merge branch 'rt/input-bufs' into lang/foundations
This commit is contained in:
commit
7187aaa4d5
3 changed files with 295 additions and 96 deletions
|
|
@ -1,5 +1,5 @@
|
|||
# load.ludic - a clip the runtime can actually open. Audio.load takes a real FILESYSTEM path (it
|
||||
# bottoms out in AVAudioPlayer), not the pack-aware file_open every other asset arrives through: in a
|
||||
# bottoms out in AVAudioFile), not the pack-aware file_open every other asset arrives through: in a
|
||||
# bundle every clip loads as 0 while Fs.exists, which IS pack-aware, says it is there. So try the
|
||||
# runtime's path, and on 0 lift the bytes out of the pack into the cache directory and load that.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,168 +1,367 @@
|
|||
; ============================================================================
|
||||
; audio.ll — the macOS audio backend, written in LLVM IR.
|
||||
;
|
||||
; Like cocoa.ll, this compiles no Objective-C and no C: it drives AVAudioPlayer
|
||||
; Like cocoa.ll, this compiles no Objective-C and no C: it drives AVAudioEngine
|
||||
; through the Objective-C runtime's C ABI (objc_getClass / sel_registerName /
|
||||
; objc_msgSend). ludicc splices it in — alongside cocoa.ll — only when a
|
||||
; windowed build actually uses Audio.* (g_uses_audio), and links AVFoundation
|
||||
; then. A sound is loaded once into an AVAudioPlayer and the opaque player
|
||||
; pointer is handed back to the Ludic side (audio.ludic), which keeps the handle
|
||||
; table; these functions just operate on one player at a time.
|
||||
; then. A sound is decoded once into a PCM buffer and given a voice of its own
|
||||
; on one shared engine; the opaque voice pointer is handed back to the Ludic
|
||||
; side (audio.ludic), which keeps the handle table.
|
||||
;
|
||||
; A voice is a player node (the buffer, scheduled again on every play), a
|
||||
; varispeed (the rate) and a small mixer (the volume and the pan), in that order
|
||||
; into the engine's main mixer. It was an AVAudioPlayer until AVFAudio was found
|
||||
; to keep a 64-byte AudioQueueOwner for good on every play after the clip had
|
||||
; finished, whatever was called around it and even after the player was
|
||||
; released: a valley's footsteps added one a play for as long as it ran. An
|
||||
; engine's voice replays with nothing kept.
|
||||
;
|
||||
; Exposed to Ludic (through the snd_* intrinsics, all is_windowed()-guarded so
|
||||
; they vanish from a headless build):
|
||||
; snd_load(path) -> player snd_play(p, loops, rate, vol)
|
||||
; snd_stop(p) snd_playing(p) -> bool
|
||||
; snd_set_volume(p, vol) snd_set_rate(p, rate) snd_set_pan(p, pan)
|
||||
; loops: 0 = once, -1 = forever. rate / vol are 16.16 fixed (value/65536).
|
||||
; snd_load(path) -> voice snd_play(v, loops, rate, vol)
|
||||
; snd_stop(v) snd_playing(v) -> bool
|
||||
; snd_set_volume(v, vol) snd_set_rate(v, rate) snd_set_pan(v, pan)
|
||||
; loops: 0 = once, -1 = forever. rate / vol / pan are 16.16 fixed (value/65536).
|
||||
; ============================================================================
|
||||
|
||||
declare ptr @objc_getClass(ptr)
|
||||
declare ptr @sel_registerName(ptr)
|
||||
declare ptr @objc_msgSend(ptr, ptr, ...)
|
||||
declare ptr @objc_autoreleasePoolPush()
|
||||
declare void @objc_autoreleasePoolPop(ptr)
|
||||
declare ptr @malloc(i64)
|
||||
declare i64 @clock_gettime_nsec_np(i32)
|
||||
|
||||
@.a_avp = private unnamed_addr constant [14 x i8] c"AVAudioPlayer\00"
|
||||
@.a_nsstr = private unnamed_addr constant [9 x i8] c"NSString\00"
|
||||
@.a_nsurl = private unnamed_addr constant [6 x i8] c"NSURL\00"
|
||||
; the one engine every voice plays through, made with the first sound loaded
|
||||
@snd_engine = internal global ptr null
|
||||
|
||||
; a voice is 64 bytes: node, varispeed, mixer, buffer (pointers at 0, 8, 16, 24), the buffer's
|
||||
; frames (i64 at 32), whether it loops (i32 at 40), its length in seconds (double at 48) and when a
|
||||
; one-shot ends on the uptime clock (i64 nanoseconds at 56)
|
||||
@CLOCK_UPTIME_RAW = internal constant i32 8
|
||||
|
||||
@.a_nsstr = private unnamed_addr constant [9 x i8] c"NSString\00"
|
||||
@.a_nsurl = private unnamed_addr constant [6 x i8] c"NSURL\00"
|
||||
@.a_utf8 = private unnamed_addr constant [22 x i8] c"stringWithUTF8String:\00"
|
||||
@.a_furl = private unnamed_addr constant [17 x i8] c"fileURLWithPath:\00"
|
||||
@.a_alloc = private unnamed_addr constant [6 x i8] c"alloc\00"
|
||||
@.a_init = private unnamed_addr constant [29 x i8] c"initWithContentsOfURL:error:\00"
|
||||
@.a_enrat = private unnamed_addr constant [15 x i8] c"setEnableRate:\00"
|
||||
@.a_prep = private unnamed_addr constant [14 x i8] c"prepareToPlay\00"
|
||||
@.a_svol = private unnamed_addr constant [11 x i8] c"setVolume:\00"
|
||||
@.a_srate = private unnamed_addr constant [9 x i8] c"setRate:\00"
|
||||
@.a_sloop = private unnamed_addr constant [18 x i8] c"setNumberOfLoops:\00"
|
||||
@.a_sctime= private unnamed_addr constant [16 x i8] c"setCurrentTime:\00"
|
||||
@.a_play = private unnamed_addr constant [5 x i8] c"play\00"
|
||||
@.a_stop = private unnamed_addr constant [5 x i8] c"stop\00"
|
||||
@.a_alloc = private unnamed_addr constant [6 x i8] c"alloc\00"
|
||||
@.a_initv = private unnamed_addr constant [5 x i8] c"init\00"
|
||||
@.a_rel = private unnamed_addr constant [8 x i8] c"release\00"
|
||||
@.c_file = private unnamed_addr constant [12 x i8] c"AVAudioFile\00"
|
||||
@.c_pcm = private unnamed_addr constant [17 x i8] c"AVAudioPCMBuffer\00"
|
||||
@.c_eng = private unnamed_addr constant [14 x i8] c"AVAudioEngine\00"
|
||||
@.c_node = private unnamed_addr constant [18 x i8] c"AVAudioPlayerNode\00"
|
||||
@.c_vs = private unnamed_addr constant [21 x i8] c"AVAudioUnitVarispeed\00"
|
||||
@.c_mix = private unnamed_addr constant [17 x i8] c"AVAudioMixerNode\00"
|
||||
@.a_finit = private unnamed_addr constant [22 x i8] c"initForReading:error:\00"
|
||||
@.a_pfmt = private unnamed_addr constant [17 x i8] c"processingFormat\00"
|
||||
@.a_len = private unnamed_addr constant [7 x i8] c"length\00"
|
||||
@.a_binit = private unnamed_addr constant [33 x i8] c"initWithPCMFormat:frameCapacity:\00"
|
||||
@.a_read = private unnamed_addr constant [22 x i8] c"readIntoBuffer:error:\00"
|
||||
@.a_mmix = private unnamed_addr constant [14 x i8] c"mainMixerNode\00"
|
||||
@.a_attach= private unnamed_addr constant [12 x i8] c"attachNode:\00"
|
||||
@.a_conn = private unnamed_addr constant [19 x i8] c"connect:to:format:\00"
|
||||
@.a_start = private unnamed_addr constant [21 x i8] c"startAndReturnError:\00"
|
||||
@.a_isrun = private unnamed_addr constant [10 x i8] c"isRunning\00"
|
||||
@.a_sched = private unnamed_addr constant [49 x i8] c"scheduleBuffer:atTime:options:completionHandler:\00"
|
||||
@.a_play = private unnamed_addr constant [5 x i8] c"play\00"
|
||||
@.a_stop = private unnamed_addr constant [5 x i8] c"stop\00"
|
||||
@.a_isply = private unnamed_addr constant [10 x i8] c"isPlaying\00"
|
||||
@.a_span = private unnamed_addr constant [8 x i8] c"setPan:\00"
|
||||
@.a_svol = private unnamed_addr constant [11 x i8] c"setVolume:\00"
|
||||
@.a_srate = private unnamed_addr constant [9 x i8] c"setRate:\00"
|
||||
@.a_span = private unnamed_addr constant [8 x i8] c"setPan:\00"
|
||||
@.a_srt = private unnamed_addr constant [11 x i8] c"sampleRate\00"
|
||||
|
||||
; Load a sound file into an AVAudioPlayer; returns the player (or null).
|
||||
; [[cls alloc] init]
|
||||
define internal ptr @snd_new(ptr %clsname) {
|
||||
entry:
|
||||
%cls = call ptr @objc_getClass(ptr %clsname)
|
||||
%sa = call ptr @sel_registerName(ptr @.a_alloc)
|
||||
%o0 = call ptr (ptr, ptr) @objc_msgSend(ptr %cls, ptr %sa)
|
||||
%si = call ptr @sel_registerName(ptr @.a_initv)
|
||||
%o = call ptr (ptr, ptr) @objc_msgSend(ptr %o0, ptr %si)
|
||||
ret ptr %o
|
||||
}
|
||||
|
||||
; the engine, made the first time a sound is loaded
|
||||
define internal ptr @snd_engine_get() {
|
||||
entry:
|
||||
%e = load ptr, ptr @snd_engine
|
||||
%have = icmp ne ptr %e, null
|
||||
br i1 %have, label %done, label %make
|
||||
make:
|
||||
%n = call ptr @snd_new(ptr @.c_eng)
|
||||
store ptr %n, ptr @snd_engine
|
||||
br label %done
|
||||
done:
|
||||
%r = load ptr, ptr @snd_engine
|
||||
ret ptr %r
|
||||
}
|
||||
|
||||
; the engine running: after a start, and again after the output device changed (which stops it)
|
||||
define internal void @snd_engine_run(ptr %e) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %e, null
|
||||
br i1 %nil, label %out, label %ask
|
||||
ask:
|
||||
%sr = call ptr @sel_registerName(ptr @.a_isrun)
|
||||
%run = call i8 (ptr, ptr) @objc_msgSend(ptr %e, ptr %sr)
|
||||
%on = icmp ne i8 %run, 0
|
||||
br i1 %on, label %out, label %start
|
||||
start:
|
||||
%ss = call ptr @sel_registerName(ptr @.a_start)
|
||||
%ok = call i8 (ptr, ptr, ptr) @objc_msgSend(ptr %e, ptr %ss, ptr null)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Load a sound file into a voice; returns the voice (or null).
|
||||
define ptr @snd_load(ptr %path) {
|
||||
entry:
|
||||
%pool = call ptr @objc_autoreleasePoolPush()
|
||||
%strcls = call ptr @objc_getClass(ptr @.a_nsstr)
|
||||
%sel_u = call ptr @sel_registerName(ptr @.a_utf8)
|
||||
%s = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %strcls, ptr %sel_u, ptr %path)
|
||||
%urlcls = call ptr @objc_getClass(ptr @.a_nsurl)
|
||||
%sel_f = call ptr @sel_registerName(ptr @.a_furl)
|
||||
%url = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %urlcls, ptr %sel_f, ptr %s)
|
||||
%avpcls = call ptr @objc_getClass(ptr @.a_avp)
|
||||
%nocls = icmp eq ptr %avpcls, null
|
||||
br i1 %nocls, label %fail, label %alloc
|
||||
alloc:
|
||||
%sel_a = call ptr @sel_registerName(ptr @.a_alloc)
|
||||
%p0 = call ptr (ptr, ptr) @objc_msgSend(ptr %avpcls, ptr %sel_a)
|
||||
%sel_i = call ptr @sel_registerName(ptr @.a_init)
|
||||
; initWithContentsOfURL:error: — pass NULL for the NSError** out-parameter.
|
||||
%p = call ptr (ptr, ptr, ptr, ptr) @objc_msgSend(ptr %p0, ptr %sel_i, ptr %url, ptr null)
|
||||
%nop = icmp eq ptr %p, null
|
||||
br i1 %nop, label %fail, label %ready
|
||||
ready:
|
||||
; enable rate control so setRate: takes effect, and pre-buffer.
|
||||
%sel_er = call ptr @sel_registerName(ptr @.a_enrat)
|
||||
%e = call ptr (ptr, ptr, i8) @objc_msgSend(ptr %p, ptr %sel_er, i8 1)
|
||||
%sel_pr = call ptr @sel_registerName(ptr @.a_prep)
|
||||
%pr = call ptr (ptr, ptr) @objc_msgSend(ptr %p, ptr %sel_pr)
|
||||
ret ptr %p
|
||||
%srel = call ptr @sel_registerName(ptr @.a_rel)
|
||||
%sa = call ptr @sel_registerName(ptr @.a_alloc)
|
||||
%fcls = call ptr @objc_getClass(ptr @.c_file)
|
||||
%nocls = icmp eq ptr %fcls, null
|
||||
br i1 %nocls, label %fail, label %open
|
||||
open:
|
||||
%f0 = call ptr (ptr, ptr) @objc_msgSend(ptr %fcls, ptr %sa)
|
||||
%sfi = call ptr @sel_registerName(ptr @.a_finit)
|
||||
%file = call ptr (ptr, ptr, ptr, ptr) @objc_msgSend(ptr %f0, ptr %sfi, ptr %url, ptr null)
|
||||
%nofile = icmp eq ptr %file, null
|
||||
br i1 %nofile, label %fail, label %read
|
||||
read:
|
||||
%spf = call ptr @sel_registerName(ptr @.a_pfmt)
|
||||
%fmt = call ptr (ptr, ptr) @objc_msgSend(ptr %file, ptr %spf)
|
||||
%sl = call ptr @sel_registerName(ptr @.a_len)
|
||||
%frames = call i64 (ptr, ptr) @objc_msgSend(ptr %file, ptr %sl)
|
||||
%fr32 = trunc i64 %frames to i32
|
||||
%pcmcls = call ptr @objc_getClass(ptr @.c_pcm)
|
||||
%b0 = call ptr (ptr, ptr) @objc_msgSend(ptr %pcmcls, ptr %sa)
|
||||
%sbi = call ptr @sel_registerName(ptr @.a_binit)
|
||||
%buf = call ptr (ptr, ptr, ptr, i32) @objc_msgSend(ptr %b0, ptr %sbi, ptr %fmt, i32 %fr32)
|
||||
%nobuf = icmp eq ptr %buf, null
|
||||
br i1 %nobuf, label %fail_file, label %fill
|
||||
fill:
|
||||
%srd = call ptr @sel_registerName(ptr @.a_read)
|
||||
%rok = call i8 (ptr, ptr, ptr, ptr) @objc_msgSend(ptr %file, ptr %srd, ptr %buf, ptr null)
|
||||
%good = icmp ne i8 %rok, 0
|
||||
br i1 %good, label %voice, label %fail_buf
|
||||
voice:
|
||||
%e = call ptr @snd_engine_get()
|
||||
%smm = call ptr @sel_registerName(ptr @.a_mmix)
|
||||
%main = call ptr (ptr, ptr) @objc_msgSend(ptr %e, ptr %smm)
|
||||
%node = call ptr @snd_new(ptr @.c_node)
|
||||
%vs = call ptr @snd_new(ptr @.c_vs)
|
||||
%mix = call ptr @snd_new(ptr @.c_mix)
|
||||
%sat = call ptr @sel_registerName(ptr @.a_attach)
|
||||
%a1 = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %e, ptr %sat, ptr %node)
|
||||
%a2 = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %e, ptr %sat, ptr %vs)
|
||||
%a3 = call ptr (ptr, ptr, ptr) @objc_msgSend(ptr %e, ptr %sat, ptr %mix)
|
||||
%sc = call ptr @sel_registerName(ptr @.a_conn)
|
||||
%c1 = call ptr (ptr, ptr, ptr, ptr, ptr) @objc_msgSend(ptr %e, ptr %sc, ptr %node, ptr %vs, ptr %fmt)
|
||||
%c2 = call ptr (ptr, ptr, ptr, ptr, ptr) @objc_msgSend(ptr %e, ptr %sc, ptr %vs, ptr %mix, ptr %fmt)
|
||||
%c3 = call ptr (ptr, ptr, ptr, ptr, ptr) @objc_msgSend(ptr %e, ptr %sc, ptr %mix, ptr %main, ptr %fmt)
|
||||
call void @snd_engine_run(ptr %e)
|
||||
%rf = call ptr (ptr, ptr) @objc_msgSend(ptr %file, ptr %srel)
|
||||
%ssr = call ptr @sel_registerName(ptr @.a_srt)
|
||||
%rate = call double (ptr, ptr) @objc_msgSend(ptr %fmt, ptr %ssr)
|
||||
%frd = sitofp i64 %frames to double
|
||||
%ratepos = fcmp ogt double %rate, 0.0
|
||||
%rate1 = select i1 %ratepos, double %rate, double 4.8e+04
|
||||
%secs = fdiv double %frd, %rate1
|
||||
%v = call ptr @malloc(i64 64)
|
||||
store ptr %node, ptr %v
|
||||
%v8 = getelementptr i8, ptr %v, i64 8
|
||||
store ptr %vs, ptr %v8
|
||||
%v16 = getelementptr i8, ptr %v, i64 16
|
||||
store ptr %mix, ptr %v16
|
||||
%v24 = getelementptr i8, ptr %v, i64 24
|
||||
store ptr %buf, ptr %v24
|
||||
%v32 = getelementptr i8, ptr %v, i64 32
|
||||
store i64 %frames, ptr %v32
|
||||
%v40 = getelementptr i8, ptr %v, i64 40
|
||||
store i32 0, ptr %v40
|
||||
%v48 = getelementptr i8, ptr %v, i64 48
|
||||
store double %secs, ptr %v48
|
||||
%v56 = getelementptr i8, ptr %v, i64 56
|
||||
store i64 0, ptr %v56
|
||||
call void @objc_autoreleasePoolPop(ptr %pool)
|
||||
ret ptr %v
|
||||
fail_buf:
|
||||
%rb = call ptr (ptr, ptr) @objc_msgSend(ptr %buf, ptr %srel)
|
||||
br label %fail_file
|
||||
fail_file:
|
||||
%rf2 = call ptr (ptr, ptr) @objc_msgSend(ptr %file, ptr %srel)
|
||||
br label %fail
|
||||
fail:
|
||||
call void @objc_autoreleasePoolPop(ptr %pool)
|
||||
ret ptr null
|
||||
}
|
||||
|
||||
; Rewind to the start and play, applying loops / rate / volume.
|
||||
define void @snd_play(ptr %p, i32 %loops, i32 %rate, i32 %vol) {
|
||||
define internal ptr @snd_part(ptr %v, i64 %off) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %p, null
|
||||
%a = getelementptr i8, ptr %v, i64 %off
|
||||
%p = load ptr, ptr %a
|
||||
ret ptr %p
|
||||
}
|
||||
|
||||
; a 16.16 value as a float
|
||||
define internal float @snd_fx(i32 %x) {
|
||||
entry:
|
||||
%f = sitofp i32 %x to float
|
||||
%r = fdiv float %f, 6.5536e+04
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; Rewind to the start and play, applying loops / rate / volume: the buffer scheduled again over
|
||||
; whatever was playing, looping when loops is -1.
|
||||
define void @snd_play(ptr %v, i32 %loops, i32 %rate, i32 %vol) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %v, null
|
||||
br i1 %nil, label %out, label %go
|
||||
go:
|
||||
%vf = sitofp i32 %vol to float
|
||||
%v = fdiv float %vf, 6.5536e+04
|
||||
%sel_v = call ptr @sel_registerName(ptr @.a_svol)
|
||||
%rv = call ptr (ptr, ptr, float) @objc_msgSend(ptr %p, ptr %sel_v, float %v)
|
||||
%rf = sitofp i32 %rate to float
|
||||
%r = fdiv float %rf, 6.5536e+04
|
||||
%sel_r = call ptr @sel_registerName(ptr @.a_srate)
|
||||
%rr = call ptr (ptr, ptr, float) @objc_msgSend(ptr %p, ptr %sel_r, float %r)
|
||||
%l64 = sext i32 %loops to i64
|
||||
%sel_l = call ptr @sel_registerName(ptr @.a_sloop)
|
||||
%rl = call ptr (ptr, ptr, i64) @objc_msgSend(ptr %p, ptr %sel_l, i64 %l64)
|
||||
%sel_ct = call ptr @sel_registerName(ptr @.a_sctime)
|
||||
%rc = call ptr (ptr, ptr, double) @objc_msgSend(ptr %p, ptr %sel_ct, double 0.0)
|
||||
%sel_p = call ptr @sel_registerName(ptr @.a_play)
|
||||
%rp = call ptr (ptr, ptr) @objc_msgSend(ptr %p, ptr %sel_p)
|
||||
; play autoreleases an AVAudioTime: its own pool, so a sound played before a frame's pool keeps nothing
|
||||
%pool = call ptr @objc_autoreleasePoolPush()
|
||||
%e = load ptr, ptr @snd_engine
|
||||
call void @snd_engine_run(ptr %e)
|
||||
%node = call ptr @snd_part(ptr %v, i64 0)
|
||||
%vs = call ptr @snd_part(ptr %v, i64 8)
|
||||
%mix = call ptr @snd_part(ptr %v, i64 16)
|
||||
%buf = call ptr @snd_part(ptr %v, i64 24)
|
||||
%sst = call ptr @sel_registerName(ptr @.a_stop)
|
||||
%r0 = call ptr (ptr, ptr) @objc_msgSend(ptr %node, ptr %sst)
|
||||
%sv = call ptr @sel_registerName(ptr @.a_svol)
|
||||
%vf = call float @snd_fx(i32 %vol)
|
||||
%r1 = call ptr (ptr, ptr, float) @objc_msgSend(ptr %mix, ptr %sv, float %vf)
|
||||
%sr = call ptr @sel_registerName(ptr @.a_srate)
|
||||
%rf = call float @snd_fx(i32 %rate)
|
||||
%r2 = call ptr (ptr, ptr, float) @objc_msgSend(ptr %vs, ptr %sr, float %rf)
|
||||
%loop = icmp eq i32 %loops, -1
|
||||
%opt = select i1 %loop, i64 3, i64 2 ; AVAudioPlayerNodeBufferLoops (1) | Interrupts (2)
|
||||
%lp = zext i1 %loop to i32
|
||||
%v40 = getelementptr i8, ptr %v, i64 40
|
||||
store i32 %lp, ptr %v40
|
||||
%ssc = call ptr @sel_registerName(ptr @.a_sched)
|
||||
%r3 = call ptr (ptr, ptr, ptr, ptr, i64, ptr) @objc_msgSend(ptr %node, ptr %ssc, ptr %buf, ptr null, i64 %opt, ptr null)
|
||||
%sp = call ptr @sel_registerName(ptr @.a_play)
|
||||
%r4 = call ptr (ptr, ptr) @objc_msgSend(ptr %node, ptr %sp)
|
||||
; a one-shot ends its length (at this rate) from now
|
||||
%v48 = getelementptr i8, ptr %v, i64 48
|
||||
%secs = load double, ptr %v48
|
||||
%rpos = fcmp ogt float %rf, 0.0
|
||||
%rf1 = select i1 %rpos, float %rf, float 1.0
|
||||
%rd = fpext float %rf1 to double
|
||||
%dur = fdiv double %secs, %rd
|
||||
%durns = fmul double %dur, 1.0e+09
|
||||
%dns = fptosi double %durns to i64
|
||||
%clk = load i32, ptr @CLOCK_UPTIME_RAW
|
||||
%now = call i64 @clock_gettime_nsec_np(i32 %clk)
|
||||
%end = add i64 %now, %dns
|
||||
%v56 = getelementptr i8, ptr %v, i64 56
|
||||
store i64 %end, ptr %v56
|
||||
call void @objc_autoreleasePoolPop(ptr %pool)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @snd_stop(ptr %p) {
|
||||
define void @snd_stop(ptr %v) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %p, null
|
||||
%nil = icmp eq ptr %v, null
|
||||
br i1 %nil, label %out, label %go
|
||||
go:
|
||||
%sel_s = call ptr @sel_registerName(ptr @.a_stop)
|
||||
%rs = call ptr (ptr, ptr) @objc_msgSend(ptr %p, ptr %sel_s)
|
||||
%sel_ct = call ptr @sel_registerName(ptr @.a_sctime)
|
||||
%rc = call ptr (ptr, ptr, double) @objc_msgSend(ptr %p, ptr %sel_ct, double 0.0)
|
||||
%node = call ptr @snd_part(ptr %v, i64 0)
|
||||
%sst = call ptr @sel_registerName(ptr @.a_stop)
|
||||
%r = call ptr (ptr, ptr) @objc_msgSend(ptr %node, ptr %sst)
|
||||
%v40 = getelementptr i8, ptr %v, i64 40
|
||||
store i32 0, ptr %v40
|
||||
%v56 = getelementptr i8, ptr %v, i64 56
|
||||
store i64 0, ptr %v56
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
define i32 @snd_playing(ptr %p) {
|
||||
; Playing while the node plays and, for a one-shot, until the time its buffer ends: a player node
|
||||
; goes on "playing" silence after its buffer, and asking it where it is made two AVAudioTime objects a
|
||||
; call that nothing gave back, so the end is worked out when it is played and only compared here.
|
||||
define i32 @snd_playing(ptr %v) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %p, null
|
||||
%nil = icmp eq ptr %v, null
|
||||
br i1 %nil, label %zero, label %ask
|
||||
ask:
|
||||
%sel_ip = call ptr @sel_registerName(ptr @.a_isply)
|
||||
%b = call i8 (ptr, ptr) @objc_msgSend(ptr %p, ptr %sel_ip)
|
||||
%bi = zext i8 %b to i32
|
||||
%r = and i32 %bi, 1
|
||||
ret i32 %r
|
||||
%node = call ptr @snd_part(ptr %v, i64 0)
|
||||
%sip = call ptr @sel_registerName(ptr @.a_isply)
|
||||
%b = call i8 (ptr, ptr) @objc_msgSend(ptr %node, ptr %sip)
|
||||
%on = icmp ne i8 %b, 0
|
||||
br i1 %on, label %loopq, label %zero
|
||||
loopq:
|
||||
%v40 = getelementptr i8, ptr %v, i64 40
|
||||
%lp = load i32, ptr %v40
|
||||
%looping = icmp ne i32 %lp, 0
|
||||
br i1 %looping, label %one, label %clock
|
||||
clock:
|
||||
%v56 = getelementptr i8, ptr %v, i64 56
|
||||
%end = load i64, ptr %v56
|
||||
%clk = load i32, ptr @CLOCK_UPTIME_RAW
|
||||
%now = call i64 @clock_gettime_nsec_np(i32 %clk)
|
||||
%left = icmp slt i64 %now, %end
|
||||
%li = zext i1 %left to i32
|
||||
ret i32 %li
|
||||
one:
|
||||
ret i32 1
|
||||
zero:
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @snd_set_volume(ptr %p, i32 %vol) {
|
||||
define void @snd_set_volume(ptr %v, i32 %vol) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %p, null
|
||||
%nil = icmp eq ptr %v, null
|
||||
br i1 %nil, label %out, label %go
|
||||
go:
|
||||
%vf = sitofp i32 %vol to float
|
||||
%v = fdiv float %vf, 6.5536e+04
|
||||
%sel_v = call ptr @sel_registerName(ptr @.a_svol)
|
||||
%rv = call ptr (ptr, ptr, float) @objc_msgSend(ptr %p, ptr %sel_v, float %v)
|
||||
%mix = call ptr @snd_part(ptr %v, i64 16)
|
||||
%sv = call ptr @sel_registerName(ptr @.a_svol)
|
||||
%f = call float @snd_fx(i32 %vol)
|
||||
%r = call ptr (ptr, ptr, float) @objc_msgSend(ptr %mix, ptr %sv, float %f)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Stereo position, -1.0 (hard left) .. 1.0 (hard right). AVAudioPlayer's own pan, so it
|
||||
; costs nothing and needs no mixer: a one-shot can be placed before it is fired.
|
||||
define void @snd_set_pan(ptr %p, i32 %pan) {
|
||||
; Stereo position, -1.0 (hard left) .. 1.0 (hard right): the voice's mixer's pan into the main
|
||||
; mixer, so a one-shot can be placed before it is fired.
|
||||
define void @snd_set_pan(ptr %v, i32 %pan) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %p, null
|
||||
%nil = icmp eq ptr %v, null
|
||||
br i1 %nil, label %out, label %go
|
||||
go:
|
||||
%pf = sitofp i32 %pan to float
|
||||
%v = fdiv float %pf, 6.5536e+04
|
||||
%sel_p = call ptr @sel_registerName(ptr @.a_span)
|
||||
%rp = call ptr (ptr, ptr, float) @objc_msgSend(ptr %p, ptr %sel_p, float %v)
|
||||
%mix = call ptr @snd_part(ptr %v, i64 16)
|
||||
%sp = call ptr @sel_registerName(ptr @.a_span)
|
||||
%f = call float @snd_fx(i32 %pan)
|
||||
%r = call ptr (ptr, ptr, float) @objc_msgSend(ptr %mix, ptr %sp, float %f)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @snd_set_rate(ptr %p, i32 %rate) {
|
||||
define void @snd_set_rate(ptr %v, i32 %rate) {
|
||||
entry:
|
||||
%nil = icmp eq ptr %p, null
|
||||
%nil = icmp eq ptr %v, null
|
||||
br i1 %nil, label %out, label %go
|
||||
go:
|
||||
%rf = sitofp i32 %rate to float
|
||||
%r = fdiv float %rf, 6.5536e+04
|
||||
%sel_r = call ptr @sel_registerName(ptr @.a_srate)
|
||||
%rr = call ptr (ptr, ptr, float) @objc_msgSend(ptr %p, ptr %sel_r, float %r)
|
||||
%vs = call ptr @snd_part(ptr %v, i64 8)
|
||||
%sr = call ptr @sel_registerName(ptr @.a_srate)
|
||||
%f = call float @snd_fx(i32 %rate)
|
||||
%r = call ptr (ptr, ptr, float) @objc_msgSend(ptr %vs, ptr %sr, float %f)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# audio.ludic — the Audio.* standard library (#22).
|
||||
#
|
||||
# A small, game-shaped sound API over the platform audio backend (audio.ll,
|
||||
# AVAudioPlayer on macOS). A sound is loaded once into a handle; sfx are fired
|
||||
# AVAudioEngine on macOS). A sound is loaded once into a handle; sfx are fired
|
||||
# one-shot, music plays on a single looping channel. Playback itself is not part
|
||||
# of the deterministic simulation — the device output is real-time and
|
||||
# non-deterministic — but every *trigger* here is an ordinary function call the
|
||||
|
|
@ -21,7 +21,7 @@ const AUDIO_CAP: int = 64 # max simultaneously-loaded sounds
|
|||
|
||||
export state RtAudioState {
|
||||
snd_ready: bool = false
|
||||
snd_tab: pointers = null # handle (1-based) -> AVAudioPlayer pointer
|
||||
snd_tab: pointers = null # handle (1-based) -> the backend's voice pointer
|
||||
snd_master: fixed = 1.0 # master volume, applied to every play (and to audio_play_at's gain)
|
||||
snd_rate: fixed = 1.0 # playback rate / pitch (1.0 = normal)
|
||||
snd_music: int = 0 # the handle currently playing as music (0 = none)
|
||||
|
|
@ -124,7 +124,7 @@ function audio_play_at(rt_audio_st: mut RtAudioState, id: int, gain: fixed, pitc
|
|||
if pn < -1.0 { pn = -1.0 }
|
||||
if pn > 1.0 { pn = 1.0 }
|
||||
var pt = pitch
|
||||
if pt < 0.25 { pt = 0.25 } # AVAudioPlayer's own rate range
|
||||
if pt < 0.25 { pt = 0.25 } # the varispeed's own rate range
|
||||
if pt > 4.0 { pt = 4.0 }
|
||||
snd_set_pan(p, pn)
|
||||
snd_play(p, 0, pt, g * rt_audio_st.snd_master)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue