/* oggrecode: re-encode an Ogg Vorbis file at a lower VBR quality, deterministically (fixed stream serial, * no tags). Used by build-edgar.sh to shrink the music and sound effects of The Legend of Edgar. * Part of the build scripts published with the game's source. Public domain / CC0. * oggrecode [mono] * Links against libogg, libvorbis, libvorbisenc and libvorbisfile. */ #include #include #include #include #include static void flush(ogg_stream_state *os, ogg_page *og, FILE *out, int force) { while ((force ? ogg_stream_flush(os, og) : ogg_stream_pageout(os, og)) > 0) { fwrite(og->header, 1, og->header_len, out); fwrite(og->body, 1, og->body_len, out); } } int main(int argc, char **argv) { OggVorbis_File vf; vorbis_info vi, *src; vorbis_comment vc; vorbis_dsp_state vd; vorbis_block vb; ogg_stream_state os; ogg_page og; ogg_packet op, h1, h2, h3; FILE *in, *out; int bitstream, channels, mono, i, j; long rate; if (argc < 4) return 2; mono = argc > 4 && strcmp(argv[4], "mono") == 0; in = fopen(argv[2], "rb"); if (!in || ov_open_callbacks(in, &vf, NULL, 0, OV_CALLBACKS_NOCLOSE) < 0) { fprintf(stderr, "bad input %s\n", argv[2]); return 1; } src = ov_info(&vf, -1); rate = src->rate; channels = mono ? 1 : src->channels; vorbis_info_init(&vi); if (vorbis_encode_init_vbr(&vi, channels, rate, (float)atof(argv[1])) != 0) return 1; vorbis_comment_init(&vc); vorbis_analysis_init(&vd, &vi); vorbis_block_init(&vd, &vb); ogg_stream_init(&os, 1); out = fopen(argv[3], "wb"); vorbis_analysis_headerout(&vd, &vc, &h1, &h2, &h3); ogg_stream_packetin(&os, &h1); ogg_stream_packetin(&os, &h2); ogg_stream_packetin(&os, &h3); flush(&os, &og, out, 1); while (1) { float **pcm; long got = ov_read_float(&vf, &pcm, 4096, &bitstream); if (got <= 0) { vorbis_analysis_wrote(&vd, 0); } else { float **dst = vorbis_analysis_buffer(&vd, got); int sch = ov_info(&vf, -1)->channels; for (j = 0; j < got; j++) { if (mono) { float s = 0; for (i = 0; i < sch; i++) s += pcm[i][j]; dst[0][j] = s / sch; } else { for (i = 0; i < channels; i++) dst[i][j] = pcm[i < sch ? i : 0][j]; } } vorbis_analysis_wrote(&vd, got); } while (vorbis_analysis_blockout(&vd, &vb) == 1) { vorbis_analysis(&vb, NULL); vorbis_bitrate_addblock(&vb); while (vorbis_bitrate_flushpacket(&vd, &op)) { ogg_stream_packetin(&os, &op); flush(&os, &og, out, 0); } } if (got <= 0) break; } flush(&os, &og, out, 1); fclose(out); ov_clear(&vf); fclose(in); return 0; }