1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

docs: add more documentation to each of the modules in vlib (#13043)

This commit is contained in:
jeffmikels 2022-01-07 06:28:50 -05:00 committed by GitHub
parent 287331bc19
commit 6e6d51a1c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 266 additions and 71 deletions

View File

@ -1 +1,8 @@
# Documentation for all included modules... # `vlib` Documentation
`vlib` is the term for all modules included by default with V and
maintained as part of the V source code repository.
Some included modules depend on third party libraries, and these are kept
separate in the `thirdparty` directory at the root level of the source
repository.

View File

@ -1,4 +1,16 @@
## Description: ## Description:
`arrays` provides some generic functions for processing arrays. `arrays` is a module that provides utility functions to make working with arrays easier.
## Examples:
```v
import arrays
fn main() {
a := [1, 5, 7, 0, 9]
assert arrays.min(a) ? == 0
assert arrays.max(a) ? == 9
assert arrays.idx_min(a) ? == 3
}
```

View File

@ -6,9 +6,10 @@ module arrays
// - merge - combine two sorted arrays and maintain sorted order // - merge - combine two sorted arrays and maintain sorted order
// - chunk - chunk array to arrays with n elements // - chunk - chunk array to arrays with n elements
// - window - get snapshots of the window of the given size sliding along array with the given step, where each snapshot is an array // - window - get snapshots of the window of the given size sliding along array with the given step, where each snapshot is an array
// - zip - concat two arrays into one map // - TODO: zip - merge two arrays by interleaving e.g. arrays.zip([1,3,5], [2,4,6]) => [1,2,3,4,5,6]
// min returns the minimum value in the array // min returns the minimum value in the array
// Example: arrays.min([1,2,3,0,9]) // => 0
pub fn min<T>(a []T) ?T { pub fn min<T>(a []T) ?T {
if a.len == 0 { if a.len == 0 {
return error('.min called on an empty array') return error('.min called on an empty array')
@ -23,6 +24,7 @@ pub fn min<T>(a []T) ?T {
} }
// max returns the maximum the maximum value in the array // max returns the maximum the maximum value in the array
// Example: arrays.max([1,2,3,0,9]) // => 9
pub fn max<T>(a []T) ?T { pub fn max<T>(a []T) ?T {
if a.len == 0 { if a.len == 0 {
return error('.max called on an empty array') return error('.max called on an empty array')
@ -37,6 +39,7 @@ pub fn max<T>(a []T) ?T {
} }
// idx_min returns the index of the minimum value in the array // idx_min returns the index of the minimum value in the array
// Example: arrays.idx_min([1,2,3,0,9]) // => 3
pub fn idx_min<T>(a []T) ?int { pub fn idx_min<T>(a []T) ?int {
if a.len == 0 { if a.len == 0 {
return error('.idx_min called on an empty array') return error('.idx_min called on an empty array')
@ -53,6 +56,7 @@ pub fn idx_min<T>(a []T) ?int {
} }
// idx_max returns the index of the maximum value in the array // idx_max returns the index of the maximum value in the array
// Example: arrays.idx_max([1,2,3,0,9]) // => 4
pub fn idx_max<T>(a []T) ?int { pub fn idx_max<T>(a []T) ?int {
if a.len == 0 { if a.len == 0 {
return error('.idx_max called on an empty array') return error('.idx_max called on an empty array')
@ -69,6 +73,7 @@ pub fn idx_max<T>(a []T) ?int {
} }
// merge two sorted arrays (ascending) and maintain sorted order // merge two sorted arrays (ascending) and maintain sorted order
// Example: arrays.merge([1,3,5,7], [2,4,6,8]) // => [1,2,3,4,5,6,7,8]
[direct_array_access] [direct_array_access]
pub fn merge<T>(a []T, b []T) []T { pub fn merge<T>(a []T, b []T) []T {
mut m := []T{len: a.len + b.len} mut m := []T{len: a.len + b.len}
@ -102,6 +107,8 @@ pub fn merge<T>(a []T, b []T) []T {
} }
// group n arrays into a single array of arrays with n elements // group n arrays into a single array of arrays with n elements
// (an error will be generated if the type annotation is omitted)
// Example: arrays.group<int>([1,2,3],[4,5,6]) // => [[1, 4], [2, 5], [3, 6]]
pub fn group<T>(lists ...[]T) [][]T { pub fn group<T>(lists ...[]T) [][]T {
mut length := if lists.len > 0 { lists[0].len } else { 0 } mut length := if lists.len > 0 { lists[0].len } else { 0 }
// calculate length of output by finding shortest input array // calculate length of output by finding shortest input array
@ -128,8 +135,8 @@ pub fn group<T>(lists ...[]T) [][]T {
return [][]T{} return [][]T{}
} }
// chunk array to arrays with n elements // chunk array into a single array of arrays where each element is the next `size` elements of the original
// example: arrays.chunk([1, 2, 3], 2) => [[1, 2], [3]] // Example: arrays.chunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 2)) // => [[1, 2], [3, 4], [5, 6], [7, 8], [9]]
pub fn chunk<T>(list []T, size int) [][]T { pub fn chunk<T>(list []T, size int) [][]T {
// allocate chunk array // allocate chunk array
mut chunks := [][]T{cap: list.len / size + if list.len % size == 0 { 0 } else { 1 }} mut chunks := [][]T{cap: list.len / size + if list.len % size == 0 { 0 } else { 1 }}
@ -163,8 +170,8 @@ pub struct WindowAttribute {
// - `size` - snapshot size // - `size` - snapshot size
// - `step` - gap size between each snapshot, default is 1. // - `step` - gap size between each snapshot, default is 1.
// //
// example A: `arrays.window([1, 2, 3, 4], size: 2)` => `[[1, 2], [2, 3], [3, 4]]` // Example: arrays.window([1, 2, 3, 4], size: 2) => [[1, 2], [2, 3], [3, 4]]
// example B: `arrays.window([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size: 3, step: 2)` => `[[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]` // Example: arrays.window([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], size: 3, step: 2) // => [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]
pub fn window<T>(list []T, attr WindowAttribute) [][]T { pub fn window<T>(list []T, attr WindowAttribute) [][]T {
// allocate snapshot array // allocate snapshot array
mut windows := [][]T{cap: list.len - attr.size + 1} mut windows := [][]T{cap: list.len - attr.size + 1}
@ -183,10 +190,11 @@ pub fn window<T>(list []T, attr WindowAttribute) [][]T {
} }
// sum up array, return nothing when array has no elements // sum up array, return nothing when array has no elements
// NOTICE: currently V has bug that cannot make sum function takes custom struct with + operator overloaded. //
// NOTICE: currently V has bug that cannot make sum function takes custom struct with + operator overloaded
// which means you can only pass array of numbers for now. // which means you can only pass array of numbers for now.
// Future work: Fix generic operator overloading detection issue. // TODO: Fix generic operator overloading detection issue.
// usage: `arrays.sum<int>([1, 2, 3, 4, 5])?` => `15` // Example: arrays.sum<int>([1, 2, 3, 4, 5])? // => 15
pub fn sum<T>(list []T) ?T { pub fn sum<T>(list []T) ?T {
if list.len == 0 { if list.len == 0 {
return error('Cannot sum up array of nothing.') return error('Cannot sum up array of nothing.')
@ -206,8 +214,8 @@ pub fn sum<T>(list []T) ?T {
} }
// accumulates values with the first element and applying providing operation to current accumulator value and each elements. // accumulates values with the first element and applying providing operation to current accumulator value and each elements.
// if the array is empty, then returns error. // If the array is empty, then returns error.
// usage: `arrays.reduce([1, 2, 3, 4, 5], fn (t1 int, t2 int) int { return t1 * t2 })?` => `120` // Example: arrays.reduce([1, 2, 3, 4, 5], fn (t1 int, t2 int) int { return t1 * t2 })? // => 120
pub fn reduce<T>(list []T, reduce_op fn (t1 T, t2 T) T) ?T { pub fn reduce<T>(list []T, reduce_op fn (t1 T, t2 T) T) ?T {
if list.len == 0 { if list.len == 0 {
return error('Cannot reduce array of nothing.') return error('Cannot reduce array of nothing.')
@ -227,7 +235,7 @@ pub fn reduce<T>(list []T, reduce_op fn (t1 T, t2 T) T) ?T {
} }
// accumulates values with providing initial value and applying providing operation to current accumulator value and each elements. // accumulates values with providing initial value and applying providing operation to current accumulator value and each elements.
// usage: `arrays.fold<string, byte>(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] })` => `149` // Example: arrays.fold<string, byte>(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] }) // => 149
pub fn fold<T, R>(list []T, init R, fold_op fn (r R, t T) R) R { pub fn fold<T, R>(list []T, init R, fold_op fn (r R, t T) R) R {
mut value := init mut value := init
@ -239,7 +247,7 @@ pub fn fold<T, R>(list []T, init R, fold_op fn (r R, t T) R) R {
} }
// flattens n + 1 dimensional array into n dimensional array // flattens n + 1 dimensional array into n dimensional array
// usage: `arrays.flatten<int>([[1, 2, 3], [4, 5]])` => `[1, 2, 3, 4, 5]` // Example: arrays.flatten<int>([[1, 2, 3], [4, 5]]) // => [1, 2, 3, 4, 5]
pub fn flatten<T>(list [][]T) []T { pub fn flatten<T>(list [][]T) []T {
// calculate required capacity // calculate required capacity
mut required_size := 0 mut required_size := 0
@ -263,7 +271,7 @@ pub fn flatten<T>(list [][]T) []T {
} }
// grouping list of elements with given key selector. // grouping list of elements with given key selector.
// usage: `arrays.assort<int, string>(['H', 'el', 'lo'], fn (v string) int { return v.len })` => `{1: ['H'], 2: ['el', 'lo']}` // Example: arrays.group_by<int, string>(['H', 'el', 'lo'], fn (v string) int { return v.len }) // => {1: ['H'], 2: ['el', 'lo']}
pub fn group_by<K, V>(list []V, grouping_op fn (v V) K) map[K][]V { pub fn group_by<K, V>(list []V, grouping_op fn (v V) K) map[K][]V {
mut result := map[K][]V{} mut result := map[K][]V{}
@ -281,7 +289,13 @@ pub fn group_by<K, V>(list []V, grouping_op fn (v V) K) map[K][]V {
return result return result
} }
// concatenate two arrays // concatenate an array with an arbitrary number of additional values
//
// NOTE: if you have two arrays, you should simply use the `<<` operator directly
// Example: arrays.concat([1, 2, 3], 4, 5, 6) == [1, 2, 3, 4, 5, 6] // => true
// Example: arrays.concat([1, 2, 3], ...[4, 5, 6]) == [1, 2, 3, 4, 5, 6] // => true
// Example: arr << [4, 5, 6] // does what you need if arr is mutable
[deprecated]
pub fn concat<T>(a []T, b ...T) []T { pub fn concat<T>(a []T, b ...T) []T {
mut m := []T{cap: a.len + b.len} mut m := []T{cap: a.len + b.len}
@ -291,7 +305,32 @@ pub fn concat<T>(a []T, b ...T) []T {
return m return m
} }
// zip returns a new array by interleaving the source arrays.
//
// NOTE: The two arrays do not need to be equal sizes
// Example: arrays.zip([1, 3, 5, 7], [2, 4, 6, 8, 10]) // => [1, 2, 3, 4, 5, 6, 7, 8, 10]
pub fn zip<T>(a []T, b []T) []T {
mut m := []T{cap: a.len + b.len}
mut i := 0
for i < m.cap {
// short-circuit the rest of the loop as soon as we can
if i >= a.len {
m << b[i..]
break
}
if i >= b.len {
m << a[i..]
break
}
m << a[i]
m << b[i]
i++
}
return m
}
// returns the smallest element >= val, requires `arr` to be sorted // returns the smallest element >= val, requires `arr` to be sorted
// Example: arrays.lower_bound([2, 4, 6, 8], 3)? // => 4
pub fn lower_bound<T>(arr []T, val T) ?T { pub fn lower_bound<T>(arr []T, val T) ?T {
if arr.len == 0 { if arr.len == 0 {
return error('.lower_bound called on an empty array') return error('.lower_bound called on an empty array')
@ -314,6 +353,7 @@ pub fn lower_bound<T>(arr []T, val T) ?T {
} }
// returns the largest element <= val, requires `arr` to be sorted // returns the largest element <= val, requires `arr` to be sorted
// Example: arrays.upper_bound([2, 4, 6, 8], 3)? // => 2
pub fn upper_bound<T>(arr []T, val T) ?T { pub fn upper_bound<T>(arr []T, val T) ?T {
if arr.len == 0 { if arr.len == 0 {
return error('.upper_bound called on an empty array') return error('.upper_bound called on an empty array')
@ -335,7 +375,10 @@ pub fn upper_bound<T>(arr []T, val T) ?T {
} }
} }
// binary search, requires `arr` to be sorted, returns index // binary search, requires `arr` to be sorted, returns index of found item or error.
// Binary searches on sorted lists can be faster than other array searches because at maximum
// the algorithm only has to traverse log N elements
// Example: arrays.binary_search([1, 2, 3, 4], 4) ? // => 3
pub fn binary_search<T>(arr []T, target T) ?int { pub fn binary_search<T>(arr []T, target T) ?int {
mut left := 0 mut left := 0
mut right := arr.len - 1 mut right := arr.len - 1

View File

@ -1,9 +1,8 @@
## Description: ## Description:
`benchmark` provides an easy way to measure how fast a piece of code is, `benchmark` provides tools for measuring and reporting on the performance of code.
and produce a readable report about it.
## Example usage of this module: ## Example 1:
```v ```v
import benchmark import benchmark
@ -25,12 +24,13 @@ bmark.stop()
println(bmark.total_message('remarks about the benchmark')) println(bmark.total_message('remarks about the benchmark'))
``` ```
benchmark.start() and b.measure() are convenience methods, `.start()` and `.measure()` are convenience methods,
intended to be used in combination. Their goal is to make intended to be used in combination. Their goal is to make
benchmarking of small snippets of code as *short*, easy to benchmarking of small snippets of code as _short_, easy to
write, and then to read and analyze the results, as possible. write, and easy to read and analyze as possible.
## Example 2:
Example:
```v ```v
import time import time
import benchmark import benchmark
@ -45,6 +45,7 @@ b.measure('code_2')
``` ```
... which will produce on stdout something like this: ... which will produce on stdout something like this:
```text ```text
SPENT 1500.063 ms in code_1 SPENT 1500.063 ms in code_1
SPENT 500.061 ms in code_2 SPENT 500.061 ms in code_2

View File

@ -1,9 +1,10 @@
## Description: ## Description:
`bitfield` is a module for manipulating arrays of bits, i.e. series of zeroes `bitfield` is a module for manipulating arrays of bits,
and ones spread across an array of storage units (unsigned 32-bit integers). i.e. series of zeroes and ones spread across an
array of storage units (unsigned 32-bit integers).
## BitField structure ## BitField Structure
Bit arrays are stored in data structures called 'BitField'. The structure is Bit arrays are stored in data structures called 'BitField'. The structure is
'opaque', i.e. its internals are not available to the end user. This module 'opaque', i.e. its internals are not available to the end user. This module

View File

@ -4,4 +4,3 @@
It implements the builtin V types `array`, `string`, `map`. It implements the builtin V types `array`, `string`, `map`.
It also implements builtin functions like `println`, `eprintln`, `malloc`, It also implements builtin functions like `println`, `eprintln`, `malloc`,
`panic`, `print_backtrace`. `panic`, `print_backtrace`.

View File

@ -6,7 +6,7 @@ declarative subcommands, each having separate set of options.
See also the `flag` module, for a simpler command line option parser, See also the `flag` module, for a simpler command line option parser,
that supports only options. that supports only options.
Usage example: ## Example:
```v ```v
module main module main

View File

@ -3,3 +3,14 @@
`clipboard` provides access to the platform's clipboard mechanism. `clipboard` provides access to the platform's clipboard mechanism.
You can use it to read from the system clipboard, and write to it You can use it to read from the system clipboard, and write to it
from your applications. from your applications.
## Examples:
```v
import clipboard
fn main() {
mut c := clipboard.new()
println(c.get_text())
}
```

View File

@ -21,7 +21,7 @@ pub fn (mut cb Clipboard) clear_all() {
cb.clear() cb.clear()
} }
// destroy destroys the clipboard and free it's resources. // destroy destroys the clipboard and frees its resources.
pub fn (mut cb Clipboard) destroy() { pub fn (mut cb Clipboard) destroy() {
cb.free() cb.free()
} }

View File

@ -1,6 +1,7 @@
## Description: ## Description:
`compress.zlib` implements zlib compression and decompression of binary data. `compress.zlib` is a module that assists in the compression and
decompression of binary data using `zlib` compression
## Examples: ## Examples:

View File

@ -8,6 +8,8 @@ pub const max_size = u64(1 << 31)
fn C.tdefl_compress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr fn C.tdefl_compress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
fn C.tinfl_decompress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr fn C.tinfl_decompress_mem_to_heap(source_buf voidptr, source_buf_len usize, out_len &usize, flags int) voidptr
// compresses an array of bytes using zlib and returns the compressed bytes in a new array
// Example: compressed := zlib.compress(b) ?
[manualfree] [manualfree]
pub fn compress(data []byte) ?[]byte { pub fn compress(data []byte) ?[]byte {
if u64(data.len) > zlib.max_size { if u64(data.len) > zlib.max_size {
@ -33,6 +35,8 @@ pub fn compress(data []byte) ?[]byte {
return copy return copy
} }
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
// Example: decompressed := zlib.decompress(b) ?
[manualfree] [manualfree]
pub fn decompress(data []byte) ?[]byte { pub fn decompress(data []byte) ?[]byte {
mut out_len := usize(0) mut out_len := usize(0)

View File

@ -1,5 +1,45 @@
## Description: ## Description:
`crypto` is a namespace for multiple crypto algorithms. `crypto` is a module that exposes cryptographic algorithms to V programs.
Each submodule implements things differently, so be sure to consider the documentation
of the specific algorithm you need, but in general, the method is to create a `cipher`
struct using one of the module functions, and then to call the `encrypt` or `decrypt`
method on that struct to actually encrypt or decrypt your data.
This module is a work-in-progress. For example, the AES implementation currently requires you
to create a destination buffer of the correct size to receive the decrypted data, and the AesCipher
`encrypt` and `decrypt` functions only operate on the first block of the `src`.
The implementations here are loosely based on [Go's crypto package](https://pkg.go.dev/crypto). The implementations here are loosely based on [Go's crypto package](https://pkg.go.dev/crypto).
## Examples:
```v
import crypto.aes
import crypto.rand
fn main() {
// remember to save this key somewhere if you ever want to decrypt your data
key := rand.read(32) ?
println('KEY: $key')
// this data is one block (16 bytes) big
mut data := 'THIS IS THE DATA'.bytes()
println('generating cipher')
cipher := aes.new_cipher(key)
println('performing encryption')
mut encrypted := []byte{len: aes.block_size}
cipher.encrypt(mut encrypted, mut data)
println(encrypted)
println('performing decryption')
mut decrypted := []byte{len: aes.block_size}
cipher.decrypt(mut decrypted, mut encrypted)
println(decrypted)
assert decrypted == data
}
```

View File

@ -13,13 +13,16 @@ pub const (
) )
// AesCipher represents an AES encryption using a particular key. // AesCipher represents an AES encryption using a particular key.
// It follows the API of golang's `cipher.Block` and is designed to
// handle only one block of data at a time. In most cases, you
// probably want to encrypt and decrypt using [[AesCbc](#AesCbc)]
struct AesCipher { struct AesCipher {
mut: mut:
enc []u32 enc []u32
dec []u32 dec []u32
} }
// new_cipher creates and returns a new `AesCipher`. // new_cipher creates and returns a new [[AesCipher](#AesCipher)].
// The key argument should be the AES key, // The key argument should be the AES key,
// either 16, 24, or 32 bytes to select // either 16, 24, or 32 bytes to select
// AES-128, AES-192, or AES-256. // AES-128, AES-192, or AES-256.
@ -43,8 +46,10 @@ pub fn (c &AesCipher) block_size() int {
return aes.block_size return aes.block_size
} }
// encrypt encrypts the blocks in `src` to `dst`. // encrypt encrypts the first block of data in `src` to `dst`.
// Please note: `dst` and `src` are both mutable for performance reasons. // NOTE: `dst` and `src` are both mutable for performance reasons.
// NOTE: `dst` and `src` must both be pre-allocated to the correct length.
// NOTE: `dst` and `src` may be the same (overlapping entirely).
pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) { pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) {
if src.len < aes.block_size { if src.len < aes.block_size {
panic('crypto.aes: input not full block') panic('crypto.aes: input not full block')
@ -60,8 +65,10 @@ pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) {
encrypt_block_generic(c.enc, mut dst, src) encrypt_block_generic(c.enc, mut dst, src)
} }
// decrypt decrypts the blocks in `src` to `dst`. // decrypt decrypts the first block of data in `src` to `dst`.
// Please note: `dst` and `src` are both mutable for performance reasons. // NOTE: `dst` and `src` are both mutable for performance reasons.
// NOTE: `dst` and `src` must both be pre-allocated to the correct length.
// NOTE: `dst` and `src` may be the same (overlapping entirely).
pub fn (c &AesCipher) decrypt(mut dst []byte, mut src []byte) { pub fn (c &AesCipher) decrypt(mut dst []byte, mut src []byte) {
if src.len < aes.block_size { if src.len < aes.block_size {
panic('crypto.aes: input not full block') panic('crypto.aes: input not full block')

View File

@ -1,29 +0,0 @@
Based on Go's crypto packages.
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -4,3 +4,54 @@
which in turn is a library of "Simple STB-style cross-platform libraries which in turn is a library of "Simple STB-style cross-platform libraries
for C and C++, written in C.", that provide access to graphics/audio/input for C and C++, written in C.", that provide access to graphics/audio/input
processing. processing.
Each `.h` file in the sokol source code is well-documented as can be seen here:
[sokol_audio.h](https://github.com/floooh/sokol/blob/master/sokol_audio.h)
## Example from `@VROOTDIR/examples/sokol/sounds/simple_sin_tones.v`:
```v
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()
}
```

View File

@ -12,9 +12,37 @@ $if linux {
#flag darwin -framework AudioToolbox #flag darwin -framework AudioToolbox
#flag windows -lole32 #flag windows -lole32
// callback function for `stream_cb` in [[C.saudio_desc](#C.saudio_desc)] when calling [audio.setup()](#setup)
// //
// sokol callback functions run in a separate thread
//
// This function will be called with a reference to the C buffer and the maximum number of frames and channels
// the audio backend is expecting in its buffer.
//
// Terms:
// - *sample* - a 32-bit floating point number from `-1.0` to `+1.0` representing the waveform amplitude at that instant
// - *frame* - one sample for each channel at that instant
//
// To determine the number of samples expected, do `num_frames * num_channels`.
// Then, write up to that many `f32` samples into `buffer` using unsafe operations.
//
// Do not write more data to the buffer than it is requesting, but you may write less. The buffer is initialized with
// zeroes, so unwritten data will result in audio silence.
// Example: unsafe { C.memcpy(buffer, &samples, samples.len * int(sizeof(f32))) }
// Example: unsafe { mut b := buffer; for i, sample in samples { b[i] = sample } }
pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int) pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int)
// callback function for `stream_userdata_cb` to use in `C.saudio_desc` when calling [audio.setup()](#setup)
//
// sokol callback functions run in a separate thread
//
// This function operates the same way as [[FNStreamingCB](#FNStreamingCB)] but it passes customizable `user_data` to the
// callback. This is the method to use if your audio data is stored in a struct or array. Identify the
// `user_data` when you call `audio.setup()` and that object will be passed to the callback as the last arg.
// Example: mut soundbuffer := []f32
// Example: soundbuffer << previously_parsed_wavfile_bytes
// Example: audio.setup(stream_userdata_cb: mycallback, user_data: soundbuffer)
// Example: fn mycallback(buffer &f32, num_frames int, num_channels int, mut sb []f32) { ... }
pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr) pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr)
pub fn (x FNStreamingCB) str() string { pub fn (x FNStreamingCB) str() string {
@ -25,7 +53,17 @@ pub fn (x FnStreamingCBWithUserData) str() string {
return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }' return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }'
} }
// only one of `stream_cb` or `stream_userdata_cb` should be used
// //
// default values (internal to sokol C library):
//
// | variable | default | note |
// | :----------- | -------: | :--------- |
// | sample_rate | 44100 | higher sample rates take more memory but are higher quality |
// | num_channels | 1 | for stereo sound, this should be 2 |
// | buffer_frames | 2048 | buffer size in frames, larger is more latency, smaller means higher CPU |
// | packet_frames | 128 | push model only, number of frames that will be pushed in each packet |
// | num_packets | 64 | for push model only, number of packets in the backend ringbuffer |
pub struct C.saudio_desc { pub struct C.saudio_desc {
sample_rate int sample_rate int
num_channels int num_channels int
@ -84,17 +122,17 @@ pub fn query() C.saudio_desc {
return C.saudio_query_desc() return C.saudio_query_desc()
} }
// audio.sample_rate - actual sample rate // audio.sample_rate - return the actual sample rate
pub fn sample_rate() int { pub fn sample_rate() int {
return C.saudio_sample_rate() return C.saudio_sample_rate()
} }
// audio.buffer_frames - return actual backend buffer size in number of frames // audio.buffer_frames - return the actual backend buffer size in number of frames
pub fn buffer_frames() int { pub fn buffer_frames() int {
return C.saudio_buffer_frames() return C.saudio_buffer_frames()
} }
// audio.channels - actual number of channels // audio.channels - return the actual number of channels
pub fn channels() int { pub fn channels() int {
return C.saudio_channels() return C.saudio_channels()
} }
@ -105,7 +143,7 @@ pub fn suspended() bool {
return C.saudio_suspended() return C.saudio_suspended()
} }
// audio.expect - get current number of frames to fill packet queue; use in combination with audio.push/2 // audio.expect - get current number of frames to fill packet queue; use in combination with audio.push
pub fn expect() int { pub fn expect() int {
return C.saudio_expect() return C.saudio_expect()
} }
@ -115,7 +153,8 @@ pub fn push(frames &f32, num_frames int) int {
return C.saudio_push(frames, num_frames) return C.saudio_push(frames, num_frames)
} }
// // audio.fclamp - helper function to 'clamp' a number to a certain range
// Example: realsample := audio.fclamp(sample, -1.0, 1.0)
[inline] [inline]
pub fn fclamp(x f32, flo f32, fhi f32) f32 { pub fn fclamp(x f32, flo f32, fhi f32) f32 {
if x > fhi { if x > fhi {
@ -127,6 +166,10 @@ pub fn fclamp(x f32, flo f32, fhi f32) f32 {
return x return x
} }
// audio.min - helper function to return the smaller of two numbers
//
// math.min returns `f32` values, this returns `int` values
// Example: smaller := audio.min(1, 5) // smaller == 1
pub fn min(x int, y int) int { pub fn min(x int, y int) int {
if x < y { if x < y {
return x return x
@ -134,6 +177,10 @@ pub fn min(x int, y int) int {
return y return y
} }
// audio.max - helper function to return the larger of two numbers
//
// math.max returns `f32` values, this returns `int` values
// Example: larger := audio.max(1, 5) // larger == 5
pub fn max(x int, y int) int { pub fn max(x int, y int) int {
if x < y { if x < y {
return y return y