diff options
Diffstat (limited to 'v_windows/v/old/examples/sokol/sounds')
| -rw-r--r-- | v_windows/v/old/examples/sokol/sounds/melody.v | 75 | ||||
| -rw-r--r-- | v_windows/v/old/examples/sokol/sounds/simple_sin_tones.v | 42 | ||||
| -rw-r--r-- | v_windows/v/old/examples/sokol/sounds/uhoh.wav | bin | 0 -> 68562 bytes | |||
| -rw-r--r-- | v_windows/v/old/examples/sokol/sounds/wav_player.v | 209 | 
4 files changed, 326 insertions, 0 deletions
diff --git a/v_windows/v/old/examples/sokol/sounds/melody.v b/v_windows/v/old/examples/sokol/sounds/melody.v new file mode 100644 index 0000000..aa5ebc4 --- /dev/null +++ b/v_windows/v/old/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/old/examples/sokol/sounds/simple_sin_tones.v b/v_windows/v/old/examples/sokol/sounds/simple_sin_tones.v new file mode 100644 index 0000000..c327117 --- /dev/null +++ b/v_windows/v/old/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/old/examples/sokol/sounds/uhoh.wav b/v_windows/v/old/examples/sokol/sounds/uhoh.wav Binary files differnew file mode 100644 index 0000000..cff9e3c --- /dev/null +++ b/v_windows/v/old/examples/sokol/sounds/uhoh.wav diff --git a/v_windows/v/old/examples/sokol/sounds/wav_player.v b/v_windows/v/old/examples/sokol/sounds/wav_player.v new file mode 100644 index 0000000..3c9419b --- /dev/null +++ b/v_windows/v/old/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 +}  | 
