From 6e6d51a1c92a4155bc7a4c6cf8ee2948c9c8ba70 Mon Sep 17 00:00:00 2001 From: jeffmikels Date: Fri, 7 Jan 2022 06:28:50 -0500 Subject: [PATCH] docs: add more documentation to each of the modules in vlib (#13043) --- vlib/README.md | 9 ++++- vlib/arrays/README.md | 14 ++++++- vlib/arrays/arrays.v | 73 ++++++++++++++++++++++++++++-------- vlib/benchmark/README.md | 15 ++++---- vlib/bitfield/README.md | 7 ++-- vlib/builtin/README.md | 1 - vlib/cli/README.md | 2 +- vlib/clipboard/README.md | 11 ++++++ vlib/clipboard/clipboard.v | 2 +- vlib/compress/zlib/README.md | 3 +- vlib/compress/zlib/zlib.v | 4 ++ vlib/crypto/README.md | 42 ++++++++++++++++++++- vlib/crypto/aes/aes.v | 17 ++++++--- vlib/crypto/readme.txt | 29 -------------- vlib/sokol/README.md | 51 +++++++++++++++++++++++++ vlib/sokol/audio/audio.v | 57 +++++++++++++++++++++++++--- 16 files changed, 266 insertions(+), 71 deletions(-) delete mode 100644 vlib/crypto/readme.txt diff --git a/vlib/README.md b/vlib/README.md index 137d7167d..f2f18533a 100644 --- a/vlib/README.md +++ b/vlib/README.md @@ -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. diff --git a/vlib/arrays/README.md b/vlib/arrays/README.md index e5bee5466..8028cd6c4 100644 --- a/vlib/arrays/README.md +++ b/vlib/arrays/README.md @@ -1,4 +1,16 @@ ## 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 +} +``` diff --git a/vlib/arrays/arrays.v b/vlib/arrays/arrays.v index 60bd8e8dc..d63d08497 100644 --- a/vlib/arrays/arrays.v +++ b/vlib/arrays/arrays.v @@ -6,9 +6,10 @@ module arrays // - merge - combine two sorted arrays and maintain sorted order // - 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 -// - 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 +// Example: arrays.min([1,2,3,0,9]) // => 0 pub fn min(a []T) ?T { if a.len == 0 { return error('.min called on an empty array') @@ -23,6 +24,7 @@ pub fn min(a []T) ?T { } // max returns the maximum the maximum value in the array +// Example: arrays.max([1,2,3,0,9]) // => 9 pub fn max(a []T) ?T { if a.len == 0 { return error('.max called on an empty array') @@ -37,6 +39,7 @@ pub fn max(a []T) ?T { } // 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(a []T) ?int { if a.len == 0 { return error('.idx_min called on an empty array') @@ -53,6 +56,7 @@ pub fn idx_min(a []T) ?int { } // 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(a []T) ?int { if a.len == 0 { return error('.idx_max called on an empty array') @@ -69,6 +73,7 @@ pub fn idx_max(a []T) ?int { } // 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] pub fn merge(a []T, b []T) []T { mut m := []T{len: a.len + b.len} @@ -102,6 +107,8 @@ pub fn merge(a []T, b []T) []T { } // 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([1,2,3],[4,5,6]) // => [[1, 4], [2, 5], [3, 6]] pub fn group(lists ...[]T) [][]T { mut length := if lists.len > 0 { lists[0].len } else { 0 } // calculate length of output by finding shortest input array @@ -128,8 +135,8 @@ pub fn group(lists ...[]T) [][]T { return [][]T{} } -// chunk array to arrays with n elements -// example: arrays.chunk([1, 2, 3], 2) => [[1, 2], [3]] +// 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, 4, 5, 6, 7, 8, 9], 2)) // => [[1, 2], [3, 4], [5, 6], [7, 8], [9]] pub fn chunk(list []T, size int) [][]T { // allocate chunk array 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 // - `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 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], size: 2) => [[1, 2], [2, 3], [3, 4]] +// 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(list []T, attr WindowAttribute) [][]T { // allocate snapshot array mut windows := [][]T{cap: list.len - attr.size + 1} @@ -183,10 +190,11 @@ pub fn window(list []T, attr WindowAttribute) [][]T { } // 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. -// Future work: Fix generic operator overloading detection issue. -// usage: `arrays.sum([1, 2, 3, 4, 5])?` => `15` +// TODO: Fix generic operator overloading detection issue. +// Example: arrays.sum([1, 2, 3, 4, 5])? // => 15 pub fn sum(list []T) ?T { if list.len == 0 { return error('Cannot sum up array of nothing.') @@ -206,8 +214,8 @@ pub fn sum(list []T) ?T { } // 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. -// usage: `arrays.reduce([1, 2, 3, 4, 5], fn (t1 int, t2 int) int { return t1 * t2 })?` => `120` +// If the array is empty, then returns error. +// Example: arrays.reduce([1, 2, 3, 4, 5], fn (t1 int, t2 int) int { return t1 * t2 })? // => 120 pub fn reduce(list []T, reduce_op fn (t1 T, t2 T) T) ?T { if list.len == 0 { return error('Cannot reduce array of nothing.') @@ -227,7 +235,7 @@ pub fn reduce(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. -// usage: `arrays.fold(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] })` => `149` +// Example: arrays.fold(['H', 'e', 'l', 'l', 'o'], 0, fn (r int, t string) int { return r + t[0] }) // => 149 pub fn fold(list []T, init R, fold_op fn (r R, t T) R) R { mut value := init @@ -239,7 +247,7 @@ pub fn fold(list []T, init R, fold_op fn (r R, t T) R) R { } // flattens n + 1 dimensional array into n dimensional array -// usage: `arrays.flatten([[1, 2, 3], [4, 5]])` => `[1, 2, 3, 4, 5]` +// Example: arrays.flatten([[1, 2, 3], [4, 5]]) // => [1, 2, 3, 4, 5] pub fn flatten(list [][]T) []T { // calculate required capacity mut required_size := 0 @@ -263,7 +271,7 @@ pub fn flatten(list [][]T) []T { } // grouping list of elements with given key selector. -// usage: `arrays.assort(['H', 'el', 'lo'], fn (v string) int { return v.len })` => `{1: ['H'], 2: ['el', 'lo']}` +// Example: arrays.group_by(['H', 'el', 'lo'], fn (v string) int { return v.len }) // => {1: ['H'], 2: ['el', 'lo']} pub fn group_by(list []V, grouping_op fn (v V) K) map[K][]V { mut result := map[K][]V{} @@ -281,7 +289,13 @@ pub fn group_by(list []V, grouping_op fn (v V) K) map[K][]V { 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(a []T, b ...T) []T { mut m := []T{cap: a.len + b.len} @@ -291,7 +305,32 @@ pub fn concat(a []T, b ...T) []T { 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(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 +// Example: arrays.lower_bound([2, 4, 6, 8], 3)? // => 4 pub fn lower_bound(arr []T, val T) ?T { if arr.len == 0 { return error('.lower_bound called on an empty array') @@ -314,6 +353,7 @@ pub fn lower_bound(arr []T, val T) ?T { } // returns the largest element <= val, requires `arr` to be sorted +// Example: arrays.upper_bound([2, 4, 6, 8], 3)? // => 2 pub fn upper_bound(arr []T, val T) ?T { if arr.len == 0 { return error('.upper_bound called on an empty array') @@ -335,7 +375,10 @@ pub fn upper_bound(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(arr []T, target T) ?int { mut left := 0 mut right := arr.len - 1 diff --git a/vlib/benchmark/README.md b/vlib/benchmark/README.md index 74c78c82b..d876cb93d 100644 --- a/vlib/benchmark/README.md +++ b/vlib/benchmark/README.md @@ -1,9 +1,8 @@ ## Description: -`benchmark` provides an easy way to measure how fast a piece of code is, -and produce a readable report about it. +`benchmark` provides tools for measuring and reporting on the performance of code. -## Example usage of this module: +## Example 1: ```v import benchmark @@ -25,12 +24,13 @@ bmark.stop() 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 -benchmarking of small snippets of code as *short*, easy to -write, and then to read and analyze the results, as possible. +benchmarking of small snippets of code as _short_, easy to +write, and easy to read and analyze as possible. + +## Example 2: -Example: ```v import time import benchmark @@ -45,6 +45,7 @@ b.measure('code_2') ``` ... which will produce on stdout something like this: + ```text SPENT 1500.063 ms in code_1 SPENT 500.061 ms in code_2 diff --git a/vlib/bitfield/README.md b/vlib/bitfield/README.md index 8585b7cdb..15c8b93d7 100644 --- a/vlib/bitfield/README.md +++ b/vlib/bitfield/README.md @@ -1,9 +1,10 @@ ## Description: -`bitfield` is a module for manipulating arrays of bits, i.e. series of zeroes -and ones spread across an array of storage units (unsigned 32-bit integers). +`bitfield` is a module for manipulating arrays of bits, +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 'opaque', i.e. its internals are not available to the end user. This module diff --git a/vlib/builtin/README.md b/vlib/builtin/README.md index f692ef44c..09af131bd 100644 --- a/vlib/builtin/README.md +++ b/vlib/builtin/README.md @@ -4,4 +4,3 @@ It implements the builtin V types `array`, `string`, `map`. It also implements builtin functions like `println`, `eprintln`, `malloc`, `panic`, `print_backtrace`. - diff --git a/vlib/cli/README.md b/vlib/cli/README.md index 7712754a0..a15ca483e 100644 --- a/vlib/cli/README.md +++ b/vlib/cli/README.md @@ -6,7 +6,7 @@ declarative subcommands, each having separate set of options. See also the `flag` module, for a simpler command line option parser, that supports only options. -Usage example: +## Example: ```v module main diff --git a/vlib/clipboard/README.md b/vlib/clipboard/README.md index c79483fa4..86e6c41d1 100644 --- a/vlib/clipboard/README.md +++ b/vlib/clipboard/README.md @@ -3,3 +3,14 @@ `clipboard` provides access to the platform's clipboard mechanism. You can use it to read from the system clipboard, and write to it from your applications. + +## Examples: + +```v +import clipboard + +fn main() { + mut c := clipboard.new() + println(c.get_text()) +} +``` diff --git a/vlib/clipboard/clipboard.v b/vlib/clipboard/clipboard.v index d82289b1d..d99009a57 100644 --- a/vlib/clipboard/clipboard.v +++ b/vlib/clipboard/clipboard.v @@ -21,7 +21,7 @@ pub fn (mut cb Clipboard) clear_all() { 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() { cb.free() } diff --git a/vlib/compress/zlib/README.md b/vlib/compress/zlib/README.md index 1b9f0495b..44b667764 100644 --- a/vlib/compress/zlib/README.md +++ b/vlib/compress/zlib/README.md @@ -1,6 +1,7 @@ ## 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: diff --git a/vlib/compress/zlib/zlib.v b/vlib/compress/zlib/zlib.v index 92f3f7847..78081635a 100644 --- a/vlib/compress/zlib/zlib.v +++ b/vlib/compress/zlib/zlib.v @@ -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.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] pub fn compress(data []byte) ?[]byte { if u64(data.len) > zlib.max_size { @@ -33,6 +35,8 @@ pub fn compress(data []byte) ?[]byte { return copy } +// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array +// Example: decompressed := zlib.decompress(b) ? [manualfree] pub fn decompress(data []byte) ?[]byte { mut out_len := usize(0) diff --git a/vlib/crypto/README.md b/vlib/crypto/README.md index 2d40d7815..829ae1b90 100644 --- a/vlib/crypto/README.md +++ b/vlib/crypto/README.md @@ -1,5 +1,45 @@ ## 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). + +## 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 +} +``` diff --git a/vlib/crypto/aes/aes.v b/vlib/crypto/aes/aes.v index 50c8deccb..b023b8f04 100644 --- a/vlib/crypto/aes/aes.v +++ b/vlib/crypto/aes/aes.v @@ -13,13 +13,16 @@ pub const ( ) // 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 { mut: enc []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, // either 16, 24, or 32 bytes to select // AES-128, AES-192, or AES-256. @@ -43,8 +46,10 @@ pub fn (c &AesCipher) block_size() int { return aes.block_size } -// encrypt encrypts the blocks in `src` to `dst`. -// Please note: `dst` and `src` are both mutable for performance reasons. +// encrypt encrypts the first block of data in `src` to `dst`. +// 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) { if src.len < aes.block_size { 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) } -// decrypt decrypts the blocks in `src` to `dst`. -// Please note: `dst` and `src` are both mutable for performance reasons. +// decrypt decrypts the first block of data in `src` to `dst`. +// 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) { if src.len < aes.block_size { panic('crypto.aes: input not full block') diff --git a/vlib/crypto/readme.txt b/vlib/crypto/readme.txt deleted file mode 100644 index 93b4cdffd..000000000 --- a/vlib/crypto/readme.txt +++ /dev/null @@ -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. diff --git a/vlib/sokol/README.md b/vlib/sokol/README.md index ac81fb343..764ad9b56 100644 --- a/vlib/sokol/README.md +++ b/vlib/sokol/README.md @@ -4,3 +4,54 @@ 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 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() +} +``` diff --git a/vlib/sokol/audio/audio.v b/vlib/sokol/audio/audio.v index f7063c4f8..86dfad7fe 100644 --- a/vlib/sokol/audio/audio.v +++ b/vlib/sokol/audio/audio.v @@ -12,9 +12,37 @@ $if linux { #flag darwin -framework AudioToolbox #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) +// 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 fn (x FNStreamingCB) str() string { @@ -25,7 +53,17 @@ pub fn (x FnStreamingCBWithUserData) str() string { 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 { sample_rate int num_channels int @@ -84,17 +122,17 @@ pub fn query() C.saudio_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 { 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 { return C.saudio_buffer_frames() } -// audio.channels - actual number of channels +// audio.channels - return the actual number of channels pub fn channels() int { return C.saudio_channels() } @@ -105,7 +143,7 @@ pub fn suspended() bool { 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 { return C.saudio_expect() } @@ -115,7 +153,8 @@ pub fn push(frames &f32, num_frames int) int { 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] pub fn fclamp(x f32, flo f32, fhi f32) f32 { if x > fhi { @@ -127,6 +166,10 @@ pub fn fclamp(x f32, flo f32, fhi f32) f32 { 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 { if x < y { return x @@ -134,6 +177,10 @@ pub fn min(x int, y int) int { 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 { if x < y { return y -- 2.30.2