math,examples: add examples/sokol/sounds/simple_sin_tone_using_audio_push.v, cleanup math

This commit is contained in:
Delyan Angelov 2025-03-04 12:19:27 +02:00
parent a59ebea523
commit cec123a0df
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
5 changed files with 102 additions and 30 deletions

View File

@ -299,6 +299,7 @@ pub fn new_test_session(_vargs string, will_compile bool) TestSession {
skip_files << 'vlib/crypto/ecdsa/example/ensure_compatibility_with_net_openssl_test.v' // requires OpenSSL
// Fails compilation with: `/usr/bin/ld: /lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command line`
skip_files << 'examples/sokol/sounds/simple_sin_tones.v'
skip_files << 'examples/sokol/sounds/simple_sin_tone_using_audio_push.v'
}
if github_job != 'misc-tooling' {
// These examples need .h files that are produced from the supplied .glsl files,

View File

@ -0,0 +1,67 @@
// import log
import math
import time
import sokol.audio
fn main() {
args := arguments()
freq := args[1] or { '417' }.int()
amplitude := args[2] or { '0.5' }.f32()
dump(freq)
dump(amplitude)
audio.setup(num_channels: 1)
sample_rate := dump(audio.sample_rate())
dump(audio.buffer_frames())
dump(audio.expect())
// create an array of frames, filled with the desired pure sine tone:
mut frames := []f32{len: sample_rate * 2} // 2 seconds
for i in 0 .. frames.len {
t := f32(i) / f32(sample_rate)
frames[i] = amplitude * math.sinf(f32(freq) * t * (2 * math.pi))
}
// play the sound by continuosly pushing samples from the generated
// array of sound frames, when there is need for more:
mut fpos := 0
for {
expected_frames := audio.expect()
if expected_frames > 0 {
written_frames := audio.push(unsafe { &frames[fpos] }, expected_frames)
fpos += written_frames
// log.info('> pushing done ... fpos: ${fpos:6} | expected_frames: ${expected_frames:6} | written: ${written:6}')
if fpos > frames.len - 2 * written_frames {
// log.info('> fpos too large: ${fpos}')
fpos = find_loop_position(fpos, frames)
// log.info('> fpos looped back to start: ${fpos}')
}
}
time.sleep(50 * time.millisecond)
}
audio.shutdown()
}
fn find_loop_position(fpos int, frames []f32) int {
return find_matching_position(frames, fpos, 0.01, 2) or {
find_matching_position(frames, fpos, 0.05, 2) or {
find_matching_position(frames, fpos, 0.1, 2) or { 0 }
}
}
}
@[direct_array_access]
fn find_matching_position(frames []f32, fpos int, tol f32, d int) ?int {
if fpos - d < 0 || fpos + d >= frames.len {
return none
}
p1, p2, p3 := frames[fpos - d], frames[fpos], frames[fpos + d]
for i in 2 .. fpos / 2 {
np1, np2, np3 := frames[i - d], frames[i], frames[i + d]
if math.tolerance(np1, p1, tol) && math.tolerance(np2, p2, tol)
&& math.tolerance(np3, p3, tol) {
return i
}
}
return none
}

View File

@ -1,5 +1,13 @@
module math
fn is_neg_int(x f64) bool {
if x < 0 {
_, xf := modf(x)
return xf == 0
}
return false
}
// gamma function computed by Stirling's formula.
// The pair of results must be multiplied together to get the actual answer.
// The multiplication is left to the caller so that, if careful, the caller can avoid

View File

@ -166,23 +166,23 @@ pub fn signbit(x f64) bool {
return f64_bits(x) & sign_mask != 0
}
// tolerance checks if a and b difference are less than or equal to the tolerance value
pub fn tolerance(a f64, b f64, tol f64) bool {
mut ee := tol
// Multiplying by ee here can underflow denormal values to zero.
// Check a==b so that at least if a and b are small and identical
// we say they match.
if a == b {
// tolerance checks if the difference between `actual` and `expected` numbers, is less than or equal to the tolerance value `tol`.
// Note: the `actual` and `expected` parameters are NOT symmetrical.
// If you compare against a known expected number, put it in the *second* argument, especially if it is 0.
pub fn tolerance(actual f64, expected f64, tol f64) bool {
// Check actual==expected so that at least if they both are small and identical we say they match.
if actual == expected {
return true
}
mut d := a - b
mut d := actual - expected
if d < 0 {
d = -d
}
// note: b is correct (expected) value, a is actual value.
// make error tolerance a fraction of b, not a.
if b != 0 {
ee = ee * b
mut ee := tol
// make error tolerance a fraction of expected, not actual.
if expected != 0 {
// Multiplying by ee here can underflow denormal values to zero.
ee *= expected
if ee < 0 {
ee = -ee
}
@ -190,14 +190,18 @@ pub fn tolerance(a f64, b f64, tol f64) bool {
return d < ee
}
// close checks if a and b are within 1e-14 of each other
pub fn close(a f64, b f64) bool {
return tolerance(a, b, 1e-14)
// close checks if `actual` and `expected` are within 1e-14 of each other
// Note: the `actual` and `expected` parameters are NOT symmetrical.
// If you compare against a known expected number, put it in the *second* argument, especially if it is 0.
pub fn close(actual f64, expected f64) bool {
return tolerance(actual, expected, 1e-14)
}
// veryclose checks if a and b are within 4e-16 of each other
pub fn veryclose(a f64, b f64) bool {
return tolerance(a, b, 4e-16)
// Note: the `actual` and `expected` parameters are NOT symmetrical.
// If you compare against a known expected number, put it in the *second* argument, especially if it is 0.
pub fn veryclose(actual f64, expected f64) bool {
return tolerance(actual, expected, 4e-16)
}
// alike checks if a and b are equal
@ -221,16 +225,3 @@ pub fn alike(a f64, b f64) bool {
}
return false
}
fn is_odd_int(x f64) bool {
xi, xf := modf(x)
return xf == 0 && (i64(xi) & 1) == 1
}
fn is_neg_int(x f64) bool {
if x < 0 {
_, xf := modf(x)
return xf == 0
}
return false
}

View File

@ -69,6 +69,11 @@ pub fn powi(a i64, b i64) i64 {
return v
}
fn is_odd_int(x f64) bool {
xi, xf := modf(x)
return xf == 0 && (i64(xi) & 1) == 1
}
// pow returns the base x, raised to the provided power y. (float64)
//
// todo(playXE): make this function work on JS backend, probably problem of JS codegen that it does not work.