aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/examples/sokol/sounds
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/examples/sokol/sounds
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/examples/sokol/sounds')
-rw-r--r--v_windows/v/examples/sokol/sounds/melody.v75
-rw-r--r--v_windows/v/examples/sokol/sounds/simple_sin_tones.v42
-rw-r--r--v_windows/v/examples/sokol/sounds/uhoh.wavbin0 -> 68562 bytes
-rw-r--r--v_windows/v/examples/sokol/sounds/wav_player.v209
4 files changed, 326 insertions, 0 deletions
diff --git a/v_windows/v/examples/sokol/sounds/melody.v b/v_windows/v/examples/sokol/sounds/melody.v
new file mode 100644
index 0000000..aa5ebc4
--- /dev/null
+++ b/v_windows/v/examples/sokol/sounds/melody.v
@@ -0,0 +1,75 @@
+import gg
+import gx
+import sokol.audio
+
+const credits = 'Based on the ByteBeat formula from: https://www.youtube.com/watch?v=V4GfkFbDojc \n "Techno" by Gabriel Miceli'
+
+struct AppState {
+mut:
+ gframe int // the current graphical frame
+ frame_0 int // offset of the current audio frames, relative to the start of the music
+ frames [2048]f32 // a copy of the last rendered audio frames
+ gg &gg.Context // used for drawing
+}
+
+fn my_audio_stream_callback(buffer &f32, num_frames int, num_channels int, mut acontext AppState) {
+ mut soundbuffer := unsafe { buffer }
+ for frame := 0; frame < num_frames; frame++ {
+ t := int(f32(acontext.frame_0 + frame) * 0.245)
+ // "Techno" by Gabriel Miceli
+ y := (t * (((t / 10 | 0) ^ ((t / 10 | 0) - 1280)) % 11) / 2 & 127) +
+ (t * (((t / 640 | 0) ^ ((t / 640 | 0) - 2)) % 13) / 2 & 127)
+ for ch := 0; ch < num_channels; ch++ {
+ idx := frame * num_channels + ch
+ unsafe {
+ a := f32(byte(y) - 127) / 255.0
+ soundbuffer[idx] = a
+ acontext.frames[idx & 2047] = a
+ }
+ }
+ }
+ acontext.frame_0 += num_frames
+}
+
+fn main() {
+ println(credits)
+ mut state := &AppState{
+ gg: 0
+ }
+ audio.setup(
+ stream_userdata_cb: my_audio_stream_callback
+ user_data: state
+ )
+ state.gg = gg.new_context(
+ bg_color: gx.rgb(50, 50, 50)
+ width: 1024
+ height: 400
+ create_window: true
+ window_title: 'ByteBeat Music'
+ frame_fn: graphics_frame
+ user_data: state
+ )
+ state.gg.run()
+ audio.shutdown()
+}
+
+fn graphics_frame(mut state AppState) {
+ state.gframe++
+ state.gg.begin()
+ state.draw()
+ state.gg.end()
+}
+
+[inline]
+fn (mut state AppState) bsample(idx int) byte {
+ return byte(127 + state.frames[(state.gframe + idx) & 2047] * 128)
+}
+
+fn (mut state AppState) draw() {
+ // first, reset and setup ortho projection
+ for x in 0 .. 1024 {
+ mut y := 100 * (state.frames[2 * x] + state.frames[2 * x + 1])
+ state.gg.draw_line(x, 200, x, 200 + y, gx.rgba(state.bsample(x), state.bsample(x + 300),
+ state.bsample(x + 700), 255))
+ }
+}
diff --git a/v_windows/v/examples/sokol/sounds/simple_sin_tones.v b/v_windows/v/examples/sokol/sounds/simple_sin_tones.v
new file mode 100644
index 0000000..c327117
--- /dev/null
+++ b/v_windows/v/examples/sokol/sounds/simple_sin_tones.v
@@ -0,0 +1,42 @@
+import time
+import math
+import sokol.audio
+
+const (
+ sw = time.new_stopwatch()
+ sw_start_ms = sw.elapsed().milliseconds()
+)
+
+[inline]
+fn sintone(periods int, frame int, num_frames int) f32 {
+ return math.sinf(f32(periods) * (2 * math.pi) * f32(frame) / f32(num_frames))
+}
+
+fn my_audio_stream_callback(buffer &f32, num_frames int, num_channels int) {
+ ms := sw.elapsed().milliseconds() - sw_start_ms
+ unsafe {
+ mut soundbuffer := buffer
+ for frame := 0; frame < num_frames; frame++ {
+ for ch := 0; ch < num_channels; ch++ {
+ idx := frame * num_channels + ch
+ if ms < 250 {
+ soundbuffer[idx] = 0.5 * sintone(20, frame, num_frames)
+ } else if ms < 300 {
+ soundbuffer[idx] = 0.5 * sintone(25, frame, num_frames)
+ } else if ms < 1500 {
+ soundbuffer[idx] *= sintone(22, frame, num_frames)
+ } else {
+ soundbuffer[idx] = 0.5 * sintone(25, frame, num_frames)
+ }
+ }
+ }
+ }
+}
+
+fn main() {
+ audio.setup(
+ stream_cb: my_audio_stream_callback
+ )
+ time.sleep(2000 * time.millisecond)
+ audio.shutdown()
+}
diff --git a/v_windows/v/examples/sokol/sounds/uhoh.wav b/v_windows/v/examples/sokol/sounds/uhoh.wav
new file mode 100644
index 0000000..cff9e3c
--- /dev/null
+++ b/v_windows/v/examples/sokol/sounds/uhoh.wav
Binary files differ
diff --git a/v_windows/v/examples/sokol/sounds/wav_player.v b/v_windows/v/examples/sokol/sounds/wav_player.v
new file mode 100644
index 0000000..3c9419b
--- /dev/null
+++ b/v_windows/v/examples/sokol/sounds/wav_player.v
@@ -0,0 +1,209 @@
+import os
+import time
+import sokol.audio
+
+struct Player {
+mut:
+ samples []f32
+ pos int
+ finished bool
+}
+
+fn main() {
+ if os.args.len < 2 {
+ eprintln('Usage: play_wav file1.wav file2.wav ...')
+ play_sounds([os.resource_abs_path('uhoh.wav')]) ?
+ exit(1)
+ }
+ play_sounds(os.args[1..]) ?
+}
+
+fn play_sounds(files []string) ? {
+ mut player := Player{}
+ player.init()
+ for f in files {
+ if !os.exists(f) || os.is_dir(f) {
+ eprintln('skipping "$f" (does not exist)')
+ continue
+ }
+ fext := os.file_ext(f).to_lower()
+ if fext != '.wav' {
+ eprintln('skipping "$f" (not a .wav file)')
+ continue
+ }
+ player.play_wav_file(f) ?
+ }
+ player.stop()
+}
+
+//
+fn audio_player_callback(buffer &f32, num_frames int, num_channels int, mut p Player) {
+ if p.finished {
+ return
+ }
+ ntotal := num_channels * num_frames
+ nremaining := p.samples.len - p.pos
+ nsamples := if nremaining < ntotal { nremaining } else { ntotal }
+ if nsamples <= 0 {
+ p.finished = true
+ return
+ }
+ unsafe { C.memcpy(buffer, &p.samples[p.pos], nsamples * int(sizeof(f32))) }
+ p.pos += nsamples
+}
+
+fn (mut p Player) init() {
+ audio.setup(
+ num_channels: 2
+ stream_userdata_cb: audio_player_callback
+ user_data: p
+ )
+}
+
+fn (mut p Player) stop() {
+ audio.shutdown()
+ p.free()
+}
+
+fn (mut p Player) play_wav_file(fpath string) ? {
+ println('> play_wav_file: $fpath')
+ samples := read_wav_file_samples(fpath) ?
+ p.finished = true
+ p.samples << samples
+ p.finished = false
+ for !p.finished {
+ time.sleep(16 * time.millisecond)
+ }
+ p.free()
+}
+
+fn (mut p Player) free() {
+ p.finished = false
+ p.samples = []f32{}
+ p.pos = 0
+}
+
+// The read_wav_file_samples function below is based on the following sources:
+// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
+// http://www.lightlink.com/tjweber/StripWav/WAVE.html
+// http://www.lightlink.com/tjweber/StripWav/Canon.html
+// https://tools.ietf.org/html/draft-ema-vpim-wav-00
+// NB: > The chunks MAY appear in any order except that the Format chunk
+// > MUST be placed before the Sound data chunk (but not necessarily
+// > contiguous to the Sound data chunk).
+struct RIFFHeader {
+ riff [4]byte
+ file_size u32
+ form_type [4]byte
+}
+
+struct RIFFChunkHeader {
+ chunk_type [4]byte
+ chunk_size u32
+ chunk_data voidptr
+}
+
+struct RIFFFormat {
+ format_tag u16 // PCM = 1; Values other than 1 indicate some form of compression.
+ nchannels u16 // Nc ; 1 = mono ; 2 = stereo
+ sample_rate u32 // F
+ avg_bytes_per_second u32 // F * M*Nc
+ nblock_align u16 // M*Nc
+ bits_per_sample u16 // 8 * M
+ cbsize u16 // Size of the extension: 22
+ valid_bits_per_sample u16 // at most 8*M
+ channel_mask u32 // Speaker position mask
+ sub_format [16]byte // GUID
+}
+
+fn read_wav_file_samples(fpath string) ?[]f32 {
+ mut res := []f32{}
+ // eprintln('> read_wav_file_samples: $fpath -------------------------------------------------')
+ mut bytes := os.read_bytes(fpath) ?
+ mut pbytes := &byte(bytes.data)
+ mut offset := u32(0)
+ rh := unsafe { &RIFFHeader(pbytes) }
+ // eprintln('rh: $rh')
+ if rh.riff != [byte(`R`), `I`, `F`, `F`]! {
+ return error('WAV should start with `RIFF`')
+ }
+ if rh.form_type != [byte(`W`), `A`, `V`, `E`]! {
+ return error('WAV should have `WAVE` form type')
+ }
+ if rh.file_size + 8 != bytes.len {
+ return error('WAV should have valid lenght')
+ }
+ offset += sizeof(RIFFHeader)
+ mut rf := &RIFFFormat(0)
+ for {
+ if offset >= bytes.len {
+ break
+ }
+ //
+ ch := unsafe { &RIFFChunkHeader(pbytes + offset) }
+ offset += 8 + ch.chunk_size
+ // eprintln('ch: $ch')
+ // eprintln('p: $pbytes | offset: $offset | bytes.len: $bytes.len')
+ // ////////
+ if ch.chunk_type == [byte(`L`), `I`, `S`, `T`]! {
+ continue
+ }
+ //
+ if ch.chunk_type == [byte(`i`), `d`, `3`, ` `]! {
+ continue
+ }
+ //
+ if ch.chunk_type == [byte(`f`), `m`, `t`, ` `]! {
+ // eprintln('`fmt ` chunk')
+ rf = unsafe { &RIFFFormat(&ch.chunk_data) }
+ // eprintln('fmt riff format: $rf')
+ if rf.format_tag != 1 {
+ return error('only PCM encoded WAVs are supported')
+ }
+ if rf.nchannels < 1 || rf.nchannels > 2 {
+ return error('only mono or stereo WAVs are supported')
+ }
+ if rf.bits_per_sample !in [u16(8), 16] {
+ return error('only 8 or 16 bits per sample WAVs are supported')
+ }
+ continue
+ }
+ //
+ if ch.chunk_type == [byte(`d`), `a`, `t`, `a`]! {
+ if rf == 0 {
+ return error('`data` chunk should be after `fmt ` chunk')
+ }
+ // eprintln('`fmt ` chunk: $rf\n`data` chunk: $ch')
+ mut doffset := 0
+ mut dp := unsafe { &byte(&ch.chunk_data) }
+ for doffset < ch.chunk_size {
+ for c := 0; c < rf.nchannels; c++ {
+ mut x := f32(0.0)
+ mut step := 0
+ ppos := unsafe { dp + doffset }
+ if rf.bits_per_sample == 8 {
+ d8 := unsafe { &byte(ppos) }
+ x = (f32(*d8) - 128) / 128.0
+ step = 1
+ doffset++
+ }
+ if rf.bits_per_sample == 16 {
+ d16 := unsafe { &i16(ppos) }
+ x = f32(*d16) / 32768.0
+ step = 2
+ }
+ doffset += step
+ if doffset < ch.chunk_size {
+ res << x
+ if rf.nchannels == 1 {
+ // Duplicating single channel mono sounds,
+ // produces a stereo sound, simplifying further processing:
+ res << x
+ }
+ }
+ }
+ }
+ }
+ }
+ return res
+}