| 1 | // vtest build: !openbsd && !sanitize-memory-clang // Fails compilation with: `ld: /lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command line` |
| 2 | // import log |
| 3 | import math |
| 4 | import time |
| 5 | import sokol.audio |
| 6 | |
| 7 | fn main() { |
| 8 | args := arguments() |
| 9 | freq := args[1] or { '417' }.int() |
| 10 | amplitude := args[2] or { '0.5' }.f32() |
| 11 | dump(freq) |
| 12 | dump(amplitude) |
| 13 | |
| 14 | audio.setup(num_channels: 1) |
| 15 | sample_rate := dump(audio.sample_rate()) |
| 16 | dump(audio.buffer_frames()) |
| 17 | dump(audio.expect()) |
| 18 | |
| 19 | // create an array of frames, filled with the desired pure sine tone: |
| 20 | mut frames := []f32{len: sample_rate * 2} // 2 seconds |
| 21 | for i in 0 .. frames.len { |
| 22 | t := f32(i) / f32(sample_rate) |
| 23 | frames[i] = amplitude * math.sinf(f32(freq) * t * (2 * math.pi)) |
| 24 | } |
| 25 | |
| 26 | // play the sound by continuosly pushing samples from the generated |
| 27 | // array of sound frames, when there is need for more: |
| 28 | mut fpos := 0 |
| 29 | for { |
| 30 | expected_frames := audio.expect() |
| 31 | if expected_frames > 0 { |
| 32 | written_frames := audio.push(unsafe { &frames[fpos] }, expected_frames) |
| 33 | fpos += written_frames |
| 34 | // log.info('> pushing done ... fpos: ${fpos:6} | expected_frames: ${expected_frames:6} | written: ${written:6}') |
| 35 | if fpos > frames.len - 2 * written_frames { |
| 36 | // log.info('> fpos too large: ${fpos}') |
| 37 | fpos = find_loop_position(fpos, frames) |
| 38 | // log.info('> fpos looped back to start: ${fpos}') |
| 39 | } |
| 40 | } |
| 41 | time.sleep(50 * time.millisecond) |
| 42 | } |
| 43 | audio.shutdown() |
| 44 | } |
| 45 | |
| 46 | fn find_loop_position(fpos int, frames []f32) int { |
| 47 | return find_matching_position(frames, fpos, 0.01, 2) or { |
| 48 | find_matching_position(frames, fpos, 0.05, 2) or { |
| 49 | find_matching_position(frames, fpos, 0.1, 2) or { 0 } |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | @[direct_array_access] |
| 55 | fn find_matching_position(frames []f32, fpos int, tol f32, d int) ?int { |
| 56 | if fpos - d < 0 || fpos + d >= frames.len { |
| 57 | return none |
| 58 | } |
| 59 | p1, p2, p3 := frames[fpos - d], frames[fpos], frames[fpos + d] |
| 60 | for i in 2 .. fpos / 2 { |
| 61 | np1, np2, np3 := frames[i - d], frames[i], frames[i + d] |
| 62 | if math.tolerance(np1, p1, tol) && math.tolerance(np2, p2, tol) |
| 63 | && math.tolerance(np3, p3, tol) { |
| 64 | return i |
| 65 | } |
| 66 | } |
| 67 | return none |
| 68 | } |
| 69 | |