Mercurial > audlegacy-plugins
view src/vorbis/vcedit.c @ 2284:d19b53359b24
cleaned up the sndfile wav plugin, currently limiting it ONLY TO WAV
PLAYBACK. if somebody is more experienced with it and wants to restore
the other formats, go ahead (maybe change the name of the plugin too?).
author | mf0102 <0102@gmx.at> |
---|---|
date | Wed, 09 Jan 2008 15:41:22 +0100 |
parents | fa9f85cebade |
children |
line wrap: on
line source
/* This program is licensed under the GNU Library General Public License, version 2, * a copy of which is included with this program (LICENCE.LGPL). * * (c) 2000-2001 Michael Smith <msmith@labyrinth.net.au> * * * Comment editing backend, suitable for use by nice frontend interfaces. * */ #include "config.h" #include <glib.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ogg/ogg.h> #include <vorbis/codec.h> #include "vcedit.h" #define CHUNKSIZE 4096 vcedit_state * vcedit_new_state(void) { return g_new0(vcedit_state, 1); } char * vcedit_error(vcedit_state * state) { return state->lasterror; } vorbis_comment * vcedit_comments(vcedit_state * state) { return state->vc; } static void vcedit_clear_internals(vcedit_state * state) { if (state->vc) { vorbis_comment_clear(state->vc); g_free(state->vc); state->vc = NULL; } if (state->os) { ogg_stream_clear(state->os); g_free(state->os); state->os = NULL; } if (state->oy) { ogg_sync_clear(state->oy); g_free(state->oy); state->oy = NULL; } if (state->vendor) { g_free(state->vendor); state->vendor = NULL; } } void vcedit_clear(vcedit_state * state) { if (state) { vcedit_clear_internals(state); g_free(state); } } /* Next two functions pulled straight from libvorbis, apart from one change * - we don't want to overwrite the vendor string. */ static void _v_writestring(oggpack_buffer * o, char *s, int len) { while (len--) { oggpack_write(o, *s++, 8); } } static int _commentheader_out(vorbis_comment * vc, char *vendor, ogg_packet * op) { oggpack_buffer opb; oggpack_writeinit(&opb); /* preamble */ oggpack_write(&opb, 0x03, 8); _v_writestring(&opb, "vorbis", 6); /* vendor */ oggpack_write(&opb, strlen(vendor), 32); _v_writestring(&opb, vendor, strlen(vendor)); /* comments */ oggpack_write(&opb, vc->comments, 32); if (vc->comments) { int i; for (i = 0; i < vc->comments; i++) { if (vc->user_comments[i]) { oggpack_write(&opb, vc->comment_lengths[i], 32); _v_writestring(&opb, vc->user_comments[i], vc->comment_lengths[i]); } else { oggpack_write(&opb, 0, 32); } } } oggpack_write(&opb, 1, 1); op->packet = _ogg_malloc(oggpack_bytes(&opb)); memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); op->bytes = oggpack_bytes(&opb); op->b_o_s = 0; op->e_o_s = 0; op->granulepos = 0; return 0; } static int _blocksize(vcedit_state * s, ogg_packet * p) { int this = vorbis_packet_blocksize(&s->vi, p); int ret = (this + s->prevW) / 4; if (!s->prevW) { s->prevW = this; return 0; } s->prevW = this; return ret; } static int _fetch_next_packet(vcedit_state * s, ogg_packet * p, ogg_page * page) { int result; char *buffer; int bytes; result = ogg_stream_packetout(s->os, p); if (result > 0) return 1; else { if (s->eosin) return 0; while (ogg_sync_pageout(s->oy, page) <= 0) { buffer = ogg_sync_buffer(s->oy, CHUNKSIZE); bytes = s->read(buffer, 1, CHUNKSIZE, s->in); ogg_sync_wrote(s->oy, bytes); if (bytes == 0) return 0; } if (ogg_page_eos(page)) s->eosin = 1; else if (ogg_page_serialno(page) != s->serial) { s->eosin = 1; s->extrapage = 1; return 0; } ogg_stream_pagein(s->os, page); return _fetch_next_packet(s, p, page); } } int vcedit_open(vcedit_state * state, VFSFile * in) { return vcedit_open_callbacks(state, (void *) in, (vcedit_read_func) aud_vfs_fread, (vcedit_write_func) aud_vfs_fwrite); } int vcedit_open_callbacks(vcedit_state * state, void *in, vcedit_read_func read_func, vcedit_write_func write_func) { char *buffer; int bytes, i; ogg_packet *header; ogg_packet header_main; ogg_packet header_comments; ogg_packet header_codebooks; ogg_page og; state->in = in; state->read = read_func; state->write = write_func; state->oy = g_new(ogg_sync_state, 1); ogg_sync_init(state->oy); buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); bytes = state->read(buffer, 1, CHUNKSIZE, state->in); ogg_sync_wrote(state->oy, bytes); if (ogg_sync_pageout(state->oy, &og) != 1) { if (bytes < CHUNKSIZE) state->lasterror = "Input truncated or empty."; else state->lasterror = "Input is not an Ogg bitstream."; goto err; } state->serial = ogg_page_serialno(&og); state->os = g_new(ogg_stream_state, 1); ogg_stream_init(state->os, state->serial); vorbis_info_init(&state->vi); state->vc = g_new(vorbis_comment, 1); vorbis_comment_init(state->vc); if (ogg_stream_pagein(state->os, &og) < 0) { state->lasterror = "Error reading first page of Ogg bitstream."; goto err; } if (ogg_stream_packetout(state->os, &header_main) != 1) { state->lasterror = "Error reading initial header packet."; goto err; } if (vorbis_synthesis_headerin(&state->vi, state->vc, &header_main) < 0) { state->lasterror = "Ogg bitstream does not contain vorbis data."; goto err; } state->mainlen = header_main.bytes; state->mainbuf = g_malloc(state->mainlen); memcpy(state->mainbuf, header_main.packet, header_main.bytes); i = 0; header = &header_comments; while (i < 2) { while (i < 2) { int result = ogg_sync_pageout(state->oy, &og); if (result == 0) break; /* Too little data so far */ else if (result == 1) { ogg_stream_pagein(state->os, &og); while (i < 2) { result = ogg_stream_packetout(state->os, header); if (result == 0) break; if (result == -1) { state->lasterror = "Corrupt secondary header."; goto err; } vorbis_synthesis_headerin(&state->vi, state->vc, header); if (i == 1) { state->booklen = header->bytes; state->bookbuf = g_malloc(state->booklen); memcpy(state->bookbuf, header->packet, header->bytes); } i++; header = &header_codebooks; } } } buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); bytes = state->read(buffer, 1, CHUNKSIZE, state->in); if (bytes == 0 && i < 2) { state->lasterror = "EOF before end of vorbis headers."; goto err; } ogg_sync_wrote(state->oy, bytes); } /* Copy the vendor tag */ state->vendor = g_malloc(strlen(state->vc->vendor) + 1); strcpy(state->vendor, state->vc->vendor); /* Headers are done! */ return 0; err: vcedit_clear_internals(state); return -1; } #if 0 static void dump_state(vcedit_state * state) { } #endif int vcedit_write(vcedit_state * state, void *out) { ogg_stream_state streamout; ogg_packet header_main; ogg_packet header_comments; ogg_packet header_codebooks; ogg_page ogout, ogin; ogg_packet op; ogg_int64_t granpos = 0; int result; char *buffer; int bytes; int needflush = 0, needout = 0; state->eosin = 0; state->extrapage = 0; header_main.bytes = state->mainlen; header_main.packet = state->mainbuf; header_main.b_o_s = 1; header_main.e_o_s = 0; header_main.granulepos = 0; header_codebooks.bytes = state->booklen; header_codebooks.packet = state->bookbuf; header_codebooks.b_o_s = 0; header_codebooks.e_o_s = 0; header_codebooks.granulepos = 0; ogg_stream_init(&streamout, state->serial); _commentheader_out(state->vc, state->vendor, &header_comments); ogg_stream_packetin(&streamout, &header_main); ogg_stream_packetin(&streamout, &header_comments); ogg_stream_packetin(&streamout, &header_codebooks); while ((result = ogg_stream_flush(&streamout, &ogout))) { if (state->write(ogout.header, 1, ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if (state->write(ogout.body, 1, ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } while (_fetch_next_packet(state, &op, &ogin)) { int size; size = _blocksize(state, &op); granpos += size; if (needflush) { if (ogg_stream_flush(&streamout, &ogout)) { if (state->write(ogout.header, 1, ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if (state->write(ogout.body, 1, ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } } else if (needout) { if (ogg_stream_pageout(&streamout, &ogout)) { if (state->write(ogout.header, 1, ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if (state->write(ogout.body, 1, ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } } needflush = needout = 0; if (op.granulepos == -1) { op.granulepos = granpos; ogg_stream_packetin(&streamout, &op); } else { /* granulepos is set, validly. Use it, and force a flush to account for shortened blocks (vcut) when appropriate */ if (granpos > op.granulepos) { granpos = op.granulepos; ogg_stream_packetin(&streamout, &op); needflush = 1; } else { ogg_stream_packetin(&streamout, &op); needout = 1; } } } streamout.e_o_s = 1; while (ogg_stream_flush(&streamout, &ogout)) { if (state->write(ogout.header, 1, ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if (state->write(ogout.body, 1, ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } /* FIXME: freeing this here probably leaks memory */ /* Done with this, now */ vorbis_info_clear(&state->vi); if (state->extrapage) { if (state->write(ogin.header, 1, ogin.header_len, out) != (size_t) ogin.header_len) goto cleanup; if (state->write(ogin.body, 1, ogin.body_len, out) != (size_t) ogin.body_len) goto cleanup; } state->eosin = 0; /* clear it, because not all paths to here do */ while (!state->eosin) { /* We reached eos, not eof */ /* We copy the rest of the stream (other logical streams) * through, a page at a time. */ while (1) { result = ogg_sync_pageout(state->oy, &ogout); if (result == 0) break; if (result < 0) state->lasterror = "Corrupt or missing data, continuing..."; else { /* Don't bother going through the rest, we can just * write the page out now */ if (state->write(ogout.header, 1, ogout.header_len, out) != (size_t) ogout.header_len) goto cleanup; if (state->write(ogout.body, 1, ogout.body_len, out) != (size_t) ogout.body_len) goto cleanup; } } buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); bytes = state->read(buffer, 1, CHUNKSIZE, state->in); ogg_sync_wrote(state->oy, bytes); if (bytes == 0) { state->eosin = 1; break; } } cleanup: ogg_stream_clear(&streamout); ogg_packet_clear(&header_comments); g_free(state->mainbuf); g_free(state->bookbuf); vcedit_clear_internals(state); if (!state->eosin) { state->lasterror = "Error writing stream to output. " "Output stream may be corrupted or truncated."; return -1; } return 0; }