Skip to content

Commit 8287d5d

Browse files
authored
thirdparty, vlib: add thirdparty/stb_vorbis; add a thin wrapper module for it too + test in encoding.vorbis (#25427)
1 parent 9d3ccf3 commit 8287d5d

13 files changed

Lines changed: 6472 additions & 1 deletion

File tree

‎examples/sokol/sounds/ogg_player.v‎

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import os
2+
import time
3+
import sokol.audio
4+
import encoding.vorbis
5+
6+
fn main() {
7+
unbuffer_stdout()
8+
if os.args.len < 2 {
9+
eprintln('Usage: ogg_player file.ogg ...')
10+
play_sounds([os.resource_abs_path('pickup.ogg')])!
11+
exit(1)
12+
}
13+
play_sounds(os.args[1..])!
14+
}
15+
16+
fn play_sounds(files []string) ! {
17+
mut player := Player{
18+
decoder: unsafe { nil }
19+
}
20+
player.init()
21+
for f in files {
22+
if !os.exists(f) || os.is_dir(f) {
23+
eprintln('skipping "${f}" (does not exist)')
24+
continue
25+
}
26+
fext := os.file_ext(f).to_lower()
27+
if fext != '.ogg' {
28+
eprintln('skipping "${f}" (not an .ogg file)')
29+
continue
30+
}
31+
player.play_ogg_file(f)!
32+
}
33+
player.stop()
34+
}
35+
36+
struct Player {
37+
mut:
38+
channels int
39+
sample_rate int
40+
pos int
41+
finished bool
42+
push_slack_ms int = 5
43+
stream_rate u32
44+
stream_channels int
45+
stream_len_samples u32
46+
stream_len_seconds f32
47+
xerror vorbis.VorbisErrorCode
48+
allocator C.stb_vorbis_alloc = C.stb_vorbis_alloc{
49+
alloc_buffer: 0
50+
alloc_buffer_length_in_bytes: 0
51+
}
52+
decoder &C.stb_vorbis // TODO: cgen error with -cstrict -gcc, when this is = unsafe { nil } here
53+
}
54+
55+
fn (mut p Player) init() {
56+
audio.setup()
57+
p.sample_rate = audio.sample_rate()
58+
p.channels = audio.channels()
59+
alloc_size := 200 * 1024
60+
p.allocator = C.stb_vorbis_alloc{
61+
alloc_buffer: unsafe { &char(vcalloc(alloc_size)) }
62+
alloc_buffer_length_in_bytes: alloc_size
63+
}
64+
}
65+
66+
fn (mut p Player) stop() {
67+
p.free()
68+
audio.shutdown()
69+
}
70+
71+
fn (mut p Player) free() {
72+
p.finished = false
73+
p.pos = 0
74+
p.close_decoder()
75+
unsafe { free(p.allocator.alloc_buffer) }
76+
unsafe {
77+
p.allocator.alloc_buffer = nil
78+
}
79+
}
80+
81+
fn (mut p Player) close_decoder() {
82+
if !isnil(p.decoder) {
83+
C.stb_vorbis_close(p.decoder)
84+
}
85+
}
86+
87+
fn (mut p Player) play_ogg_file(fpath string) ! {
88+
p.close_decoder()
89+
p.pos = 0
90+
p.xerror = .no_error
91+
p.decoder = C.stb_vorbis_open_filename(&char(fpath.str), voidptr(&p.xerror), &p.allocator)
92+
if isnil(p.decoder) || p.xerror != .no_error {
93+
return error('could not open ogg file: ${fpath}, xerror: ${p.xerror}')
94+
}
95+
info := C.stb_vorbis_get_info(p.decoder)
96+
p.stream_rate = info.sample_rate
97+
p.stream_channels = info.channels
98+
p.stream_len_samples = C.stb_vorbis_stream_length_in_samples(p.decoder)
99+
p.stream_len_seconds = C.stb_vorbis_stream_length_in_seconds(p.decoder)
100+
p.finished = false
101+
102+
if !(p.channels == p.stream_channels && p.sample_rate == p.stream_rate) {
103+
audio.shutdown()
104+
audio.setup(
105+
num_channels: p.stream_channels
106+
sample_rate: int(p.stream_rate)
107+
)
108+
p.sample_rate = audio.sample_rate()
109+
p.channels = audio.channels()
110+
}
111+
println('> play_ogg_file: rate: ${p.sample_rate:5}, channels: ${p.channels:1} | stream rate: ${p.stream_rate:5}, channels: ${p.stream_channels:1}, samples: ${p.stream_len_samples:8} | seconds: ${p.stream_len_seconds:7.3f} | ${fpath}')
112+
113+
frames := [16384]f32{}
114+
pframes := unsafe { &frames[0] }
115+
for !p.finished {
116+
mut delay := p.push_slack_ms
117+
expected_frames := audio.expect()
118+
if expected_frames > 0 {
119+
mut decoded_frames := 0
120+
for decoded_frames < expected_frames {
121+
samples := C.stb_vorbis_get_samples_float_interleaved(p.decoder, p.channels,
122+
pframes, 1024)
123+
if samples == 0 {
124+
p.finished = true
125+
break
126+
}
127+
written_frames := audio.push(pframes, samples)
128+
decoded_frames += written_frames
129+
p.pos += samples
130+
}
131+
delay = (1_000 * decoded_frames) / p.sample_rate
132+
}
133+
print('\r position: ${p.pos:9} / ${p.stream_len_samples:-9} samples | ${p.pos * p.stream_len_seconds / p.stream_len_samples:7.3f} / ${p.stream_len_seconds:-7.3f} seconds')
134+
time.sleep(int_max(p.push_slack_ms, delay - p.push_slack_ms) * time.millisecond)
135+
}
136+
println('')
137+
}

‎examples/sokol/sounds/pickup.ogg‎

4.38 KB
Binary file not shown.

‎thirdparty/stb_vorbis/.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.o

‎thirdparty/stb_vorbis/README.md‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Source: https://github.com/nothings/stb/blob/1ee679ca2ef753a528db5ba6801e1067b40481b8/stb_vorbis.c, 2021-07-12
2+
3+
Ogg Vorbis audio decoder - v1.22 - public domain, http://nothings.org/stb_vorbis/
4+
5+
Original version written by Sean Barrett in 2007.
6+
7+
Originally sponsored by RAD Game Tools. Seeking implementation
8+
sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,
9+
Elias Software, Aras Pranckevicius, and Sean Barrett.
10+
11+
Copyright (c) 2017 Sean Barrett
12+
Permission is hereby granted, free of charge, to any person obtaining a copy of
13+
this software and associated documentation files (the "Software"), to deal in
14+
the Software without restriction, including without limitation the rights to
15+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
16+
of the Software, and to permit persons to whom the Software is furnished to do
17+
so, subject to the following conditions:
18+
The above copyright notice and this permission notice shall be included in all
19+
copies or substantial portions of the Software.
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+
SOFTWARE.

0 commit comments

Comments
 (0)