| 1 | // Copyright (c) 2025 Delyan Angelov. All rights reserved. Use of this source code |
| 2 | // is governed by an MIT license that can be found in the LICENSE file. |
| 3 | module vorbis |
| 4 | |
| 5 | #flag -I @VEXEROOT/thirdparty/stb_vorbis |
| 6 | #flag @VEXEROOT/thirdparty/stb_vorbis/stb_vorbis.o |
| 7 | #include "stb_vorbis.h" |
| 8 | |
| 9 | @[typedef] |
| 10 | pub struct C.stb_vorbis_alloc { |
| 11 | pub mut: |
| 12 | alloc_buffer &char |
| 13 | alloc_buffer_length_in_bytes i32 |
| 14 | } |
| 15 | |
| 16 | @[typedef] |
| 17 | pub struct C.stb_vorbis {} |
| 18 | |
| 19 | @[typedef] |
| 20 | pub struct C.stb_vorbis_info { |
| 21 | pub mut: |
| 22 | sample_rate u32 |
| 23 | channels i32 |
| 24 | setup_memory_required u32 |
| 25 | setup_temp_memory_required u32 |
| 26 | max_frame_size i32 |
| 27 | } |
| 28 | |
| 29 | @[typedef] |
| 30 | struct C.stb_vorbis_comment { |
| 31 | pub mut: |
| 32 | vendor &char |
| 33 | comment_list_length i32 |
| 34 | comment_list &&char |
| 35 | } |
| 36 | |
| 37 | pub enum VorbisErrorCode { |
| 38 | no_error |
| 39 | need_more_data = 1 // not a real error |
| 40 | invalid_api_mixing // can not mix API modes |
| 41 | out_of_memory // not enough memory |
| 42 | not_supported // uses floor 0 |
| 43 | too_many_channels // STB_VORBIS_MAX_CHANNELS is too small |
| 44 | file_open_failure // fopen() failed |
| 45 | seek_without_length // can't seek in unknown-length file |
| 46 | unexpected_eof = 10 // file is truncated? |
| 47 | seek_invalid // seek past EOF |
| 48 | vorbis_invalid_setup = 20 // vorbis decoding error (corrupt/invalid stream) |
| 49 | vorbis_invalid_stream // vorbis decoding error |
| 50 | ogg_missing_capture_pattern = 30 |
| 51 | ogg_invalid_stream_structure_version |
| 52 | ogg_continued_packet_flag_invalid |
| 53 | ogg_incorrect_stream_serial_number |
| 54 | ogg_invalid_first_page |
| 55 | ogg_bad_packet_type |
| 56 | ogg_cant_find_last_page |
| 57 | ogg_seek_failed |
| 58 | ogg_skeleton_not_supported |
| 59 | } |
| 60 | |
| 61 | // stb_vorbis_get_info retrieve general information about the file |
| 62 | pub fn C.stb_vorbis_get_info(f &C.stb_vorbis) C.stb_vorbis_info |
| 63 | |
| 64 | // stb_vorbis_get_comment retrieve ogg comments |
| 65 | pub fn C.stb_vorbis_get_comment(f &C.stb_vorbis) C.stb_vorbis_comment |
| 66 | |
| 67 | // stb_vorbis_get_error return the last detected error (and clears it too) |
| 68 | pub fn C.stb_vorbis_get_error(f &C.stb_vorbis) VorbisErrorCode |
| 69 | |
| 70 | // stb_vorbis_close closes an ogg vorbis file and frees all the memory used for it |
| 71 | pub fn C.stb_vorbis_close(f &C.stb_vorbis) |
| 72 | |
| 73 | // stb_vorbis_get_sample_offset returns the offset in samples, from the start of the file, |
| 74 | // that will be returned by the next decode, if it is known or -1 otherwise. |
| 75 | // NOTE: NOT WORKING YET after a seek with PULLDATA API |
| 76 | pub fn C.stb_vorbis_get_sample_offset(f &C.stb_vorbis) i32 |
| 77 | |
| 78 | // stb_vorbis_get_file_offset returns the current seek point within the file, or offset |
| 79 | // from the beginning of the memory buffer. In pushdata mode it returns 0. |
| 80 | pub fn C.stb_vorbis_get_file_offset(f &C.stb_vorbis) u32 |
| 81 | |
| 82 | // stb_vorbis_open_pushdata create a vorbis decoder by passing in the initial data block |
| 83 | // containing the ogg&vorbis headers (you don't need to do parse them, just provide |
| 84 | // the first N bytes of the file--you're told if it's not enough, see below) |
| 85 | // on success, returns an stb_vorbis *, does not set error, returns the amount of |
| 86 | // data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; |
| 87 | // on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed |
| 88 | // if returns NULL and *error is VORBIS_need_more_data, then the input block was |
| 89 | // incomplete and you need to pass in a larger block from the start of the file |
| 90 | pub fn C.stb_vorbis_open_pushdata(const_datablock &u8, datablock_length_in_bytes i32, datablock_memory_consumed_in_bytes &i32, xerror &VorbisErrorCode, const_alloc_buffer &C.stb_vorbis_alloc) &C.stb_vorbis |
| 91 | |
| 92 | // stb_vorbis_decode_frame_pushdata decode a frame of audio sample data if possible from the passed-in data block |
| 93 | // Returns the number of bytes we used from `const_datablock`. |
| 94 | // possible cases: |
| 95 | // 0 bytes used, 0 samples output (need more data) |
| 96 | // N bytes used, 0 samples output (resynching the stream, keep going) |
| 97 | // N bytes used, M samples output (one frame of data) |
| 98 | // note that after opening a file, you will ALWAYS get one N-bytes,0-sample |
| 99 | // frame, because Vorbis always "discards" the first frame. |
| 100 | // |
| 101 | // Note that on resynch, stb_vorbis will rarely consume all of the buffer, |
| 102 | // instead only datablock_length_in_bytes-3 or less. This is because it wants |
| 103 | // to avoid missing parts of a page header if they cross a datablock boundary, |
| 104 | // without writing state-machiney code to record a partial detection. |
| 105 | // |
| 106 | // The number of channels returned are stored in *channels (which can be |
| 107 | // NULL -- it is always the same as the number of channels reported by |
| 108 | // get_info). *output will contain an array of float* buffers, one per |
| 109 | // channel. In other words, (*output)[0][0] contains the first sample from |
| 110 | // the first channel, and (*output)[1][0] contains the first sample from |
| 111 | // the second channel. |
| 112 | // |
| 113 | // *output points into stb_vorbis's internal output buffer storage; these |
| 114 | // buffers are owned by stb_vorbis and application code should not free |
| 115 | // them or modify their contents. They are transient and will be overwritten |
| 116 | // once you ask for more data to get decoded, so be sure to grab any data |
| 117 | // you need before then. |
| 118 | // `channels` is a place to write number of float * buffers |
| 119 | // `output` is a place to write float ** array of float * buffers |
| 120 | // `samples` is a place to write number of output samples |
| 121 | pub fn C.stb_vorbis_decode_frame_pushdata(f &C.stb_vorbis, const_datablock &u8, datablock_length_in_bytes i32, channels &i32, output &&&f32, samples &i32) i32 |
| 122 | |
| 123 | // stb_vorbis_flush_pushdata inform stb_vorbis that your next datablock will not be contiguous with |
| 124 | // previous ones (e.g. you've seeked in the data); future attempts to decode |
| 125 | // frames will cause stb_vorbis to resynchronize (as noted above), and |
| 126 | // once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it |
| 127 | // will begin decoding the _next_ frame. |
| 128 | // If you want to seek using pushdata, you need to seek in your file, then |
| 129 | // call stb_vorbis_flush_pushdata(), then start calling decoding, then once |
| 130 | // decoding is returning you data, call stb_vorbis_get_sample_offset, and |
| 131 | // if you don't like the result, seek your file again and repeat. |
| 132 | pub fn C.stb_vorbis_flush_pushdata(f &C.stb_vorbis) |
| 133 | |
| 134 | // stb_vorbis_decode_filename assumes stb_vorbis is allowed to pull data |
| 135 | // from a source: either a block of memory containing the _entire_ vorbis stream, |
| 136 | // or a &C.FILE that you or it create, or possibly some other reading mechanism |
| 137 | // if you go modify the source to replace the &C.FILE case with some kind |
| 138 | // of callback to your code. (But if you don't support seeking, you may |
| 139 | // just want to go ahead and use pushdata.) |
| 140 | // The return value is the number of samples decoded, or -1 if the file could not be opened or was not an ogg vorbis file. |
| 141 | // When you're done with it, just C.free() the pointer returned in *output. |
| 142 | pub fn C.stb_vorbis_decode_filename(const_filename &char, channels &i32, sample_rate &i32, output &&i16) i32 |
| 143 | |
| 144 | // stb_vorbis_decode_memory decodes an entire file and output the data interleaved |
| 145 | // into a malloc()ed buffer stored in *output. |
| 146 | // The return value is the number of samples decoded, or -1 if the file could not be opened or was not an ogg vorbis file. |
| 147 | // When you're done with it, just C.free() the pointer returned in *output. |
| 148 | pub fn C.stb_vorbis_decode_memory(const_mem &u8, len i32, channels &i32, sample_rate &i32, output &&i16) i32 |
| 149 | |
| 150 | // stb_vorbis_open_memory creates an ogg vorbis decoder from an ogg vorbis stream |
| 151 | // in memory (note this must be the entire stream!). |
| 152 | // On failure, returns NULL and sets *error |
| 153 | pub fn C.stb_vorbis_open_memory(const_data &u8, len i32, xerror &VorbisErrorCode, const_alloc_buffer &C.stb_vorbis_alloc) &C.stb_vorbis |
| 154 | |
| 155 | // stb_vorbis_open_filename creates an ogg vorbis decoder from a filename via fopen(). |
| 156 | // On failure, returns NULL and sets *error (possibly to VORBIS_file_open_failure). |
| 157 | pub fn C.stb_vorbis_open_filename(const_filename &char, xerror &VorbisErrorCode, const_alloc_buffer &C.stb_vorbis_alloc) &C.stb_vorbis |
| 158 | |
| 159 | // stb_vorbis_open_file creates an ogg vorbis decoder from an open &C.FILE, |
| 160 | // looking for a stream at the _current_ seek point (ftell). |
| 161 | // On failure, returns NULL and sets *error. |
| 162 | // Note: stb_vorbis must "own" this stream; if you seek it in between |
| 163 | // calls to stb_vorbis, it will become confused. Moreover, if you attempt to |
| 164 | // perform stb_vorbis_seek_*() operations on this file, it will assume it |
| 165 | // owns the _entire_ rest of the file after the start point. Use the next |
| 166 | // function, stb_vorbis_open_file_section(), to limit it. |
| 167 | pub fn C.stb_vorbis_open_file(f &C.FILE, close_handle_on_close i32, xerror &VorbisErrorCode, const_alloc_buffer &C.stb_vorbis_alloc) &C.stb_vorbis |
| 168 | |
| 169 | // stb_vorbis_open_file_section creates an ogg vorbis decoder from an open &C.FILE, |
| 170 | // looking for a stream at the _current_ seek point (ftell). |
| 171 | // The stream will be of length 'len' bytes. |
| 172 | // On failure, returns NULL and sets *error. |
| 173 | // Note: stb_vorbis must "own" this stream; if you seek it in between calls to stb_vorbis, |
| 174 | // it will become confused. |
| 175 | pub fn C.stb_vorbis_open_file_section(f &C.FILE, close_handle_on_close i32, xerror &VorbisErrorCode, const_alloc_buffer &C.stb_vorbis_alloc, len u32) &C.stb_vorbis |
| 176 | |
| 177 | // stb_vorbis_seek_frame seeks in the Vorbis file to (approximately) 'sample_number'. |
| 178 | // After calling seek_frame(), the next call to get_frame_*() will include |
| 179 | // the specified sample. |
| 180 | pub fn C.stb_vorbis_seek_frame(f &C.stb_vorbis, sample_number u32) i32 |
| 181 | |
| 182 | // stb_vorbis_seek seeks in the Vorbis file to (approximately) 'sample_number'. |
| 183 | // After calling stb_vorbis_seek(), the next call to stb_vorbis_get_samples_* |
| 184 | // will start with the specified sample. If you do not need to seek to EXACTLY |
| 185 | // the target sample when using get_samples_*, you can also use seek_frame(). |
| 186 | pub fn C.stb_vorbis_seek(f &C.stb_vorbis, sample_number u32) i32 |
| 187 | |
| 188 | // stb_vorbis_seek_start is equivalent to stb_vorbis_seek(f,0) |
| 189 | pub fn C.stb_vorbis_seek_start(f &C.stb_vorbis) i32 |
| 190 | |
| 191 | // stb_vorbis_stream_length_in_samples returns the total length of the vorbis stream in samples |
| 192 | pub fn C.stb_vorbis_stream_length_in_samples(f &C.stb_vorbis) u32 |
| 193 | |
| 194 | // stb_vorbis_stream_length_in_seconds returns the total length of the vorbis stream in seconds |
| 195 | pub fn C.stb_vorbis_stream_length_in_seconds(f &C.stb_vorbis) f32 |
| 196 | |
| 197 | // stb_vorbis_get_frame_float decodes the next frame and returns the number of samples. |
| 198 | // The number of channels returned are stored in *channels (which can be NULL; |
| 199 | // it is always the same as the number of channels reported by get_info). |
| 200 | // `*output` will contain an array of float* buffers, one per channel. |
| 201 | // These outputs will be overwritten on the next call to stb_vorbis_get_frame_*. |
| 202 | // You generally should not intermix calls to stb_vorbis_get_frame_*() |
| 203 | // and stb_vorbis_get_samples_*(), since the latter calls the former. |
| 204 | pub fn C.stb_vorbis_get_frame_float(f &C.stb_vorbis, channels &i32, output &&&f32) i32 |
| 205 | |
| 206 | // stb_vorbis_get_frame_short_interleaved decodes the next frame and returns the number |
| 207 | // of *samples* per channel. |
| 208 | // Note that for interleaved data, you pass in the number of shorts (the |
| 209 | // size of your array), but the return value is the number of samples per |
| 210 | // channel, not the total number of samples.// |
| 211 | // The data is coerced to the number of channels you request according to the |
| 212 | // channel coercion rules (see below). You must pass in the size of your |
| 213 | // buffer(s) so that stb_vorbis will not overwrite the end of the buffer. |
| 214 | // The maximum buffer size needed can be gotten from get_info(); however, |
| 215 | // the Vorbis I specification implies an absolute maximum of 4096 samples |
| 216 | // per channel. |
| 217 | // Channel coercion rules: |
| 218 | // Let M be the number of channels requested, and N the number of channels present, |
| 219 | // and Cn be the nth channel; let stereo L be the sum of all L and center channels, |
| 220 | // and stereo R be the sum of all R and center channels (channel assignment from the |
| 221 | // vorbis spec). |
| 222 | // M N output |
| 223 | // 1 k sum(Ck) for all k |
| 224 | // 2 * stereo L, stereo R |
| 225 | // k l k > l, the first l channels, then 0s |
| 226 | // k l k <= l, the first k channels |
| 227 | // Note that this is not _good_ surround etc. mixing at all! It's just so |
| 228 | // you get something useful. |
| 229 | pub fn C.stb_vorbis_get_frame_short_interleaved(f &C.stb_vorbis, num_c i32, buffer &i16, num_shorts i32) i32 |
| 230 | |
| 231 | // stb_vorbis_get_frame_short similar to stb_vorbis_get_frame_short_interleaved, but without interleaving. |
| 232 | pub fn C.stb_vorbis_get_frame_short(f &C.stb_vorbis, num_c i32, buffer &&i16, num_samples i32) i32 |
| 233 | |
| 234 | // stb_vorbis_get_samples_float_interleaved gets num_floats, not necessarily on a frame boundary. |
| 235 | // This requires buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. |
| 236 | // Returns the number of samples stored per channel; it may be less than requested |
| 237 | // at the end of the file. If there are no more samples in the file, returns 0. |
| 238 | pub fn C.stb_vorbis_get_samples_float_interleaved(f &C.stb_vorbis, channels i32, buffer &f32, num_floats i32) i32 |
| 239 | |
| 240 | // stb_vorbis_get_samples_float gets num_samples samples, not necessarily on a frame boundary. |
| 241 | // See also stb_vorbis_get_samples_float_interleaved. |
| 242 | pub fn C.stb_vorbis_get_samples_float(f &C.stb_vorbis, channels i32, buffer &&f32, num_samples i32) i32 |
| 243 | |
| 244 | // stb_vorbis_get_samples_short_interleaved gets num_samples samples, not necessarily on a frame boundary. |
| 245 | // This requires buffering so you have to supply the buffers. Applies the coercion rules above |
| 246 | // to produce 'channels' channels. Returns the number of samples stored per channel; |
| 247 | // it may be less than requested at the end of the file. If there are no more |
| 248 | // samples in the file, returns 0. |
| 249 | pub fn C.stb_vorbis_get_samples_short_interleaved(f &C.stb_vorbis, channels i32, buffer &i16, num_shorts i32) i32 |
| 250 | |
| 251 | // stb_vorbis_get_samples_short is similar to stb_vorbis_get_samples_short_interleaved . |
| 252 | pub fn C.stb_vorbis_get_samples_short(f &C.stb_vorbis, channels i32, buffer &&i16, num_samples i32) i32 |
| 253 | |