mirror of
https://github.com/exoticorn/microw8.git
synced 2026-01-20 11:16:42 +01:00
Compare commits
3 Commits
4bdbfc70d2
...
efbc5f19f5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efbc5f19f5 | ||
| 0878aa3f02 | |||
|
|
937ccf60c9 |
6
examples/tinygo/build.sh
Executable file
6
examples/tinygo/build.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
tinygo build -o cart.wasm -target target.json ./main.go
|
||||||
|
uw8 filter-exports cart.wasm cart.wasm && \
|
||||||
|
#wasm-opt -Oz --fast-math --strip-producers -o cart.wasm cart.wasm && \
|
||||||
|
uw8 pack -l 9 cart.wasm cart.uw8
|
||||||
38
examples/tinygo/main.go
Normal file
38
examples/tinygo/main.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:wasm-module env
|
||||||
|
//export atan2
|
||||||
|
func atan2(x, y float32) float32
|
||||||
|
|
||||||
|
//go:wasm-module env
|
||||||
|
//export time
|
||||||
|
func time() float32
|
||||||
|
|
||||||
|
func sqrt(v float32) float32 {
|
||||||
|
return float32(math.Sqrt(float64(v)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var FRAMEBUFFER = (*[320 * 240]byte)(unsafe.Pointer(uintptr(120)))
|
||||||
|
|
||||||
|
//export upd
|
||||||
|
func upd() {
|
||||||
|
var i int
|
||||||
|
for i < 320*240 {
|
||||||
|
t := time() * 63.0
|
||||||
|
x := float32(i%320 - 160)
|
||||||
|
y := float32(i/320 - 120)
|
||||||
|
d := float32(40000.0) / sqrt(x*x+y*y)
|
||||||
|
u := atan2(x, y) * 512.0 / 3.141
|
||||||
|
c := uint8((int(d+t*2.0) ^ int(u+t)) >> 4)
|
||||||
|
FRAMEBUFFER[i] = c
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
||||||
23
examples/tinygo/target.json
Normal file
23
examples/tinygo/target.json
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"llvm-target": "wasm32--wasi",
|
||||||
|
"build-tags": [ "tinygo.wasm" ],
|
||||||
|
"goos": "js",
|
||||||
|
"goarch": "wasm",
|
||||||
|
"linker": "wasm-ld",
|
||||||
|
"libc": "wasi-libc",
|
||||||
|
"cflags": [
|
||||||
|
"--target=wasm32--wasi",
|
||||||
|
"--sysroot={root}/lib/wasi-libc/sysroot",
|
||||||
|
"-Oz"
|
||||||
|
],
|
||||||
|
"ldflags": [
|
||||||
|
"--no-entry",
|
||||||
|
"--export-all",
|
||||||
|
"--import-memory",
|
||||||
|
"--initial-memory=262144",
|
||||||
|
"--global-base=81920",
|
||||||
|
"-zstack-size=4096",
|
||||||
|
"--strip-all"
|
||||||
|
],
|
||||||
|
"wasm-abi": "js"
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ use std::sync::{mpsc, Arc, Mutex};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{thread, time::Instant};
|
use std::{thread, time::Instant};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use cpal::traits::*;
|
use cpal::traits::*;
|
||||||
use rubato::Resampler;
|
use rubato::Resampler;
|
||||||
use uw8_window::{Window, WindowConfig};
|
use uw8_window::{Window, WindowConfig};
|
||||||
@@ -328,26 +328,38 @@ fn init_sound(
|
|||||||
let mut configs: Vec<_> = device
|
let mut configs: Vec<_> = device
|
||||||
.supported_output_configs()?
|
.supported_output_configs()?
|
||||||
.filter(|config| {
|
.filter(|config| {
|
||||||
config.channels() == 2
|
config.channels() <= 2
|
||||||
&& (config.sample_format() == cpal::SampleFormat::F32
|
&& (config.sample_format() == cpal::SampleFormat::F32
|
||||||
|| config.sample_format() == cpal::SampleFormat::I16)
|
|| config.sample_format() == cpal::SampleFormat::I16)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
if configs.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"No suitable audio output config found on device \"{}\", available configs:",
|
||||||
|
device.name()?
|
||||||
|
);
|
||||||
|
for config in device.supported_output_configs()? {
|
||||||
|
eprintln!(" {}ch {}", config.channels(), config.sample_format());
|
||||||
|
}
|
||||||
|
bail!("Failed to configure audio out");
|
||||||
|
}
|
||||||
|
|
||||||
configs.sort_by_key(|config| {
|
configs.sort_by_key(|config| {
|
||||||
let rate = 44100
|
let rate = 44100
|
||||||
.max(config.min_sample_rate().0)
|
.max(config.min_sample_rate().0)
|
||||||
.min(config.max_sample_rate().0);
|
.min(config.max_sample_rate().0);
|
||||||
let prio = if rate >= 44100 {
|
let rate_prio = if rate >= 44100 {
|
||||||
rate - 44100
|
rate - 44100
|
||||||
} else {
|
} else {
|
||||||
(44100 - rate) * 1000
|
(44100 - rate) * 1000
|
||||||
};
|
};
|
||||||
prio + (config.sample_format() == cpal::SampleFormat::I16) as u32
|
let format_prio = (config.sample_format() == cpal::SampleFormat::I16) as u32;
|
||||||
|
let channels_prio = (config.channels() != 2) as u32 * 16777216;
|
||||||
|
rate_prio + format_prio + channels_prio
|
||||||
});
|
});
|
||||||
let config = configs
|
let config = configs.into_iter().next().unwrap();
|
||||||
.into_iter()
|
|
||||||
.next()
|
|
||||||
.ok_or_else(|| anyhow!("Could not find float or 16bit signed output config"))?;
|
|
||||||
let sample_rate = cpal::SampleRate(44100)
|
let sample_rate = cpal::SampleRate(44100)
|
||||||
.max(config.min_sample_rate())
|
.max(config.min_sample_rate())
|
||||||
.min(config.max_sample_rate());
|
.min(config.max_sample_rate());
|
||||||
@@ -359,6 +371,7 @@ fn init_sound(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let sample_format = config.sample_format();
|
let sample_format = config.sample_format();
|
||||||
|
let num_channels = config.channels();
|
||||||
let config = cpal::StreamConfig {
|
let config = cpal::StreamConfig {
|
||||||
buffer_size,
|
buffer_size,
|
||||||
..config.config()
|
..config.config()
|
||||||
@@ -392,7 +405,7 @@ fn init_sound(
|
|||||||
let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30);
|
let mut pending_updates: Vec<RegisterUpdate> = Vec::with_capacity(30);
|
||||||
let mut current_time = 0;
|
let mut current_time = 0;
|
||||||
|
|
||||||
let mut callback = move |mut outer_buffer: &mut [f32], _: &_| {
|
let mut callback = move |mut outer_buffer: &mut [f32]| {
|
||||||
let mut first_update = true;
|
let mut first_update = true;
|
||||||
while let Ok(update) = rx.try_recv() {
|
while let Ok(update) = rx.try_recv() {
|
||||||
if first_update {
|
if first_update {
|
||||||
@@ -484,36 +497,83 @@ fn init_sound(
|
|||||||
current_time = current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
|
current_time = current_time.wrapping_add((step_size * 500 / sample_rate).max(1) as i32);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let stream = if sample_format == cpal::SampleFormat::F32 {
|
|
||||||
device.build_output_stream(
|
|
||||||
&config,
|
|
||||||
callback,
|
|
||||||
move |err| {
|
|
||||||
dbg!(err);
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)?
|
|
||||||
} else {
|
|
||||||
device.build_output_stream(
|
|
||||||
&config,
|
|
||||||
move |mut buffer: &mut [i16], info| {
|
|
||||||
let mut float_buffer = [0f32; 256];
|
|
||||||
|
|
||||||
while !buffer.is_empty() {
|
fn f32_to_i16<F>(mut buffer: &mut [i16], callback: &mut F)
|
||||||
let step_size = buffer.len().min(float_buffer.len());
|
where
|
||||||
let step_buffer = &mut float_buffer[..step_size];
|
F: FnMut(&mut [f32]),
|
||||||
callback(step_buffer, info);
|
{
|
||||||
for (dest, src) in buffer.iter_mut().take(step_size).zip(step_buffer.iter()) {
|
let mut float_buffer = [0f32; 256];
|
||||||
*dest = (src.max(-1.0).min(1.0) * 32767.0) as i16;
|
|
||||||
}
|
while !buffer.is_empty() {
|
||||||
buffer = &mut buffer[step_size..];
|
let step_size = buffer.len().min(float_buffer.len());
|
||||||
}
|
let step_buffer = &mut float_buffer[..step_size];
|
||||||
},
|
callback(step_buffer);
|
||||||
move |err| {
|
for (dest, src) in buffer.iter_mut().take(step_size).zip(step_buffer.iter()) {
|
||||||
dbg!(err);
|
*dest = (src.max(-1.0).min(1.0) * 32767.0) as i16;
|
||||||
},
|
}
|
||||||
None,
|
buffer = &mut buffer[step_size..];
|
||||||
)?
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stereo_to_mono<F>(mut buffer: &mut [f32], callback: &mut F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut [f32]),
|
||||||
|
{
|
||||||
|
let mut in_buffer = [0f32; 256];
|
||||||
|
|
||||||
|
while !buffer.is_empty() {
|
||||||
|
let step_size = buffer.len().min(in_buffer.len() / 2);
|
||||||
|
let step_buffer = &mut in_buffer[..step_size * 2];
|
||||||
|
callback(step_buffer);
|
||||||
|
for (index, dest) in buffer.iter_mut().take(step_size).enumerate() {
|
||||||
|
*dest = (step_buffer[index * 2] + step_buffer[index * 2 + 1]) * 0.5;
|
||||||
|
}
|
||||||
|
buffer = &mut buffer[step_size..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let stream = if sample_format == cpal::SampleFormat::F32 {
|
||||||
|
if num_channels == 2 {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [f32], _| callback(buffer),
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [f32], _| stereo_to_mono(buffer, &mut callback),
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if num_channels == 2 {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [i16], _| f32_to_i16(buffer, &mut callback),
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
} else {
|
||||||
|
device.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |buffer: &mut [i16], _| {
|
||||||
|
f32_to_i16(buffer, &mut |b| stereo_to_mono(b, &mut callback))
|
||||||
|
},
|
||||||
|
move |err| {
|
||||||
|
dbg!(err);
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)?
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Uw8Sound { stream, tx })
|
Ok(Uw8Sound { stream, tx })
|
||||||
|
|||||||
Reference in New Issue
Block a user