mirror of
https://github.com/QB64-Phoenix-Edition/QB64pe.git
synced 2024-09-20 02:04:44 +00:00
261 lines
7.5 KiB
C
261 lines
7.5 KiB
C
#define STB_DEFINE
|
|
#define STB_ONLY
|
|
#include "stb.h" /* http://nothings.org/stb.h */
|
|
#include "stb_vorbis.c"
|
|
|
|
void write_floats(FILE *g, int len, float *left, float *right);
|
|
void show_info(stb_vorbis *v);
|
|
|
|
// stb_vorbis_decode_filename: decode an entire file to interleaved shorts
|
|
void test_decode_filename(FILE *g, char *filename)
|
|
{
|
|
short *decoded;
|
|
int channels, len;
|
|
len = stb_vorbis_decode_filename(filename, &channels, &decoded);
|
|
if (len)
|
|
fwrite(decoded, 2, len*channels, g);
|
|
else
|
|
stb_fatal("Couldn't open {%s}", filename);
|
|
}
|
|
|
|
void test_get_frame_short_interleaved(FILE *g, char *filename)
|
|
{
|
|
short sbuffer[8000];
|
|
int n, error;
|
|
stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
|
|
if (!v) stb_fatal("Couldn't open {%s}", filename);
|
|
show_info(v);
|
|
|
|
while (0 != (n=stb_vorbis_get_frame_short_interleaved(v, 2, sbuffer, 8000))) {
|
|
fwrite(sbuffer, 2, n*2, g);
|
|
}
|
|
stb_vorbis_close(v);
|
|
}
|
|
|
|
void test_get_samples_short_interleaved(FILE *g, char *filename)
|
|
{
|
|
int error;
|
|
stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
|
|
if (!v) stb_fatal("Couldn't open {%s}", filename);
|
|
show_info(v);
|
|
|
|
for(;;) {
|
|
int16 sbuffer[333];
|
|
int n;
|
|
n = stb_vorbis_get_samples_short_interleaved(v, 2, sbuffer, 333);
|
|
if (n == 0)
|
|
break;
|
|
fwrite(sbuffer, 2, n*2, g);
|
|
}
|
|
stb_vorbis_close(v);
|
|
}
|
|
|
|
void test_get_frame_float(FILE *g, char *filename)
|
|
{
|
|
int error;
|
|
stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
|
|
if (!v) stb_fatal("Couldn't open {%s}", filename);
|
|
show_info(v);
|
|
|
|
for(;;) {
|
|
int n;
|
|
float *left, *right;
|
|
float **outputs;
|
|
int num_c;
|
|
n = stb_vorbis_get_frame_float(v, &num_c, &outputs);
|
|
if (n == 0)
|
|
break;
|
|
left = outputs[0];
|
|
right = outputs[num_c > 1];
|
|
write_floats(g, n, left, right);
|
|
}
|
|
stb_vorbis_close(v);
|
|
}
|
|
|
|
|
|
// in push mode, you can load your data any way you want, then
|
|
// feed it a little bit at a time. this is the preferred way to
|
|
// handle reading from a packed file or some custom stream format;
|
|
// instead of putting callbacks inside stb_vorbis, you just keep
|
|
// a little buffer (it needs to be big enough for one packet of
|
|
// audio, except at the beginning where you need to buffer up the
|
|
// entire header).
|
|
//
|
|
// for this test, I just load all the data and just lie to stb_vorbis
|
|
// and claim I only have a little of it
|
|
void test_decode_frame_pushdata(FILE *g, char *filename)
|
|
{
|
|
int p,q, len, error, used;
|
|
stb_vorbis *v;
|
|
uint8 *data = stb_file(filename, &len);
|
|
|
|
if (!data) stb_fatal("Couldn't open {%s}", filename);
|
|
|
|
p = 0;
|
|
q = 1;
|
|
retry:
|
|
v = stb_vorbis_open_pushdata(data, q, &used, &error, NULL);
|
|
if (v == NULL) {
|
|
if (error == VORBIS_need_more_data) {
|
|
q += 1;
|
|
goto retry;
|
|
}
|
|
fprintf(stderr, "Error %d\n", error);
|
|
exit(1);
|
|
}
|
|
p += used;
|
|
|
|
show_info(v);
|
|
|
|
for(;;) {
|
|
int k=0;
|
|
int n;
|
|
float *left, *right;
|
|
uint32 next_t=0;
|
|
|
|
float **outputs;
|
|
int num_c;
|
|
q = 32;
|
|
retry3:
|
|
if (q > len-p) q = len-p;
|
|
used = stb_vorbis_decode_frame_pushdata(v, data+p, q, &num_c, &outputs, &n);
|
|
if (used == 0) {
|
|
if (p+q == len) break; // no more data, stop
|
|
if (q < 128) q = 128;
|
|
q *= 2;
|
|
goto retry3;
|
|
}
|
|
p += used;
|
|
if (n == 0) continue; // seek/error recovery
|
|
left = outputs[0];
|
|
right = num_c > 1 ? outputs[1] : outputs[0];
|
|
write_floats(g, n, left, right);
|
|
}
|
|
stb_vorbis_close(v);
|
|
}
|
|
|
|
void write_floats(FILE *g, int len, float *left, float *right)
|
|
{
|
|
const float scale = 32768;
|
|
int j;
|
|
for (j=0; j < len; ++j) {
|
|
int16 x,y;
|
|
x = (int) stb_clamp((int) (scale * left[j]), -32768, 32767);
|
|
y = (int) stb_clamp((int) (scale * right[j]), -32768, 32767);
|
|
fwrite(&x, 2, 1, g);
|
|
fwrite(&y, 2, 1, g);
|
|
}
|
|
}
|
|
|
|
void show_info(stb_vorbis *v)
|
|
{
|
|
if (v) {
|
|
stb_vorbis_info info = stb_vorbis_get_info(v);
|
|
printf("%d channels, %d samples/sec\n", info.channels, info.sample_rate);
|
|
printf("Predicted memory needed: %d (%d + %d)\n", info.setup_memory_required + info.temp_memory_required,
|
|
info.setup_memory_required, info.temp_memory_required);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
FILE *g=NULL;
|
|
char *outfile = "vorbis_test.out";
|
|
char *infile;
|
|
int n=0;
|
|
|
|
if (argc > 1 && argv[1][1] == 0 && argv[1][0] >= '1' && argv[1][0] <= '5')
|
|
n = atoi(argv[1]);
|
|
|
|
if (argc < 3 || argc > 4 || n == 0) {
|
|
stbprint("Usage: sample {code} {vorbis-filename} [{output-filename}]\n"
|
|
"Code is one of:\n"
|
|
" 1 - test stb_vorbis_decode_filename\n"
|
|
" 2 - test stb_vorbis_get_frame_short_interleaved\n"
|
|
" 3 - test stb_vorbis_get_samples_short_interleaved\n"
|
|
" 4 - test stb_vorbis_get_frame_float\n"
|
|
" 5 - test stb_vorbis_decode_frame_pushdata\n");
|
|
return argc != 1;
|
|
}
|
|
|
|
infile = argv[2];
|
|
|
|
if (argc > 3)
|
|
outfile = argv[3];
|
|
if (strlen(outfile) >= 4 && 0==stb_stricmp(outfile + strlen(outfile) - 4, ".ogg"))
|
|
stb_fatal("You specified a .ogg file as your output file, which you probably didn't actually want.");
|
|
|
|
if (!strcmp(outfile, "stdout") || !strcmp(outfile, "-") || !strcmp(outfile, "-stdout"))
|
|
g = stdout;
|
|
else
|
|
g = fopen(outfile, "wb");
|
|
if (!g) stb_fatal("Couldn't open {%s} for writing", outfile);
|
|
|
|
switch (n) {
|
|
case 1: test_decode_filename(g, infile); break;
|
|
case 2: test_get_frame_short_interleaved(g, infile); break;
|
|
case 3: test_get_samples_short_interleaved(g, infile); break;
|
|
case 4: test_get_frame_float(g, infile); break;
|
|
case 5: test_decode_frame_pushdata(g, infile); break;
|
|
default: stb_fatal("Unknown option {%d}", n);
|
|
}
|
|
fclose(g);
|
|
return 0;
|
|
}
|
|
|
|
void test_push_mode_forever(FILE *g, char *filename)
|
|
{
|
|
int p,q, len, error, used;
|
|
uint8 *data = stb_file(filename, &len);
|
|
stb_vorbis *v;
|
|
|
|
if (!data) stb_fatal("Couldn't open {%s}", filename);
|
|
|
|
p = 0;
|
|
q = 1;
|
|
retry:
|
|
v = stb_vorbis_open_pushdata(data, q, &used, &error, NULL);
|
|
if (v == NULL) {
|
|
if (error == VORBIS_need_more_data) {
|
|
q += 1;
|
|
goto retry;
|
|
}
|
|
printf("Error %d\n", error);
|
|
exit(1);
|
|
}
|
|
p += used;
|
|
|
|
show_info(v);
|
|
|
|
for(;;) {
|
|
int k=0;
|
|
int n;
|
|
float *left, *right;
|
|
float **outputs;
|
|
int num_c;
|
|
q = 32;
|
|
retry3:
|
|
if (q > len-p) q = len-p;
|
|
used = stb_vorbis_decode_frame_pushdata(v, data+p, q, &num_c, &outputs, &n);
|
|
if (used == 0) {
|
|
if (p+q == len) {
|
|
// seek randomly when at end... this makes sense when listening to it, but dumb when writing to file
|
|
p = stb_rand();
|
|
if (p < 0) p = -p;
|
|
p %= (len - 8000);
|
|
stb_vorbis_flush_pushdata(v);
|
|
q = 128;
|
|
goto retry3;
|
|
}
|
|
if (q < 128) q = 128;
|
|
q *= 2;
|
|
goto retry3;
|
|
}
|
|
p += used;
|
|
if (n == 0) continue;
|
|
left = outputs[0];
|
|
right = num_c > 1 ? outputs[1] : outputs[0];
|
|
write_floats(g, n, left, right);
|
|
}
|
|
stb_vorbis_close(v);
|
|
}
|