/* sqOgg.c -- Ogg Vorbis plugin * * Copyright (c) 2006 Takashi Yamamiya * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include "OggPlugin.h" static time_t thisSession; /************************************************************ * Squeak Interface *************************************************************/ /* Validate SqOgg pointer * - oggp : [in] SqOggPtr * return value: SqOgg object or * NULL if it is invalid object. */ SqOgg * validate(SqOggPtr * oggp) { if ((oggp != NULL) && (oggp->ogg != NULL) && (oggp->sessionID == thisSession)) { return oggp->ogg; } else { return NULL; } } /************************************************************ * Low level Ogg operation *************************************************************/ /* Set new data to the buffer * - buffer : [in] data * - size: [in] size of the data */ void writeBuffer(SqOgg * ogg, const char * buffer, size_t bytes) { char * ogg_buffer= ogg_sync_buffer(&ogg->sync_state, bytes); memcpy(ogg_buffer, buffer, bytes); ogg_sync_wrote(&ogg->sync_state, bytes); } /* Get next page and set into ogg->page * return value : SQ_OGG_SUCCESS if successed * SQ_OGG_NEED_MORE if more data is needed */ int get_next_page(SqOgg * ogg) { /* get a page from the buffer */ int result= ogg_sync_pageout(&ogg->sync_state, &ogg->page); if (result == 0) return SQ_OGG_NEED_MORE; /* need more data */ if (result < 0) { /* missing or corrupt data at this page position */ fprintf(stderr,"Corrupt or missing data in bitstream; " "continuing...\n"); return SQ_OGG_NEED_MORE; } if (ogg->page_callback) (ogg->page_callback)(&ogg->page); return SQ_OGG_SUCCESS; } /* Get next packet * - packet : [out] next pakcet * return value : SQ_OGG_SUCCESS if successed * SQ_OGG_NEED_MORE if more data is needed */ int SqOggPacketNext(SqOggPtr * oggp, ogg_packet * packet) { int result; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR; /* UNDOCUMENTED */ while(1) { if (ogg->packetStatus == SQ_STREAM_INITIALIZED) { result= ogg_stream_packetout(&ogg->stream_state, packet); if (result == 1) { return SQ_OGG_SUCCESS; } else if (result == -1) { /* There are a gap (TODO: maybe SQ_OGG_ERROR is better, but because the caller doesn't handle the error well.) */ return SQ_OGG_NEED_MORE; } } result= get_next_page(ogg); if (result == SQ_OGG_NEED_MORE) return SQ_OGG_NEED_MORE; if (ogg->packetStatus == SQ_STREAM_UNINITIALIZED) { /* Initialize the logical stream */ ogg_stream_init(&ogg->stream_state, ogg_page_serialno(&ogg->page)); ogg->packetStatus= SQ_STREAM_INITIALIZED; } /* build packets from the page */ ogg_stream_pagein(&ogg->stream_state, &ogg->page); } } /************************************************************ * SqOggResult Object *************************************************************/ /* Add new buffer in a list with specific size * - head : [in] top of the list, or NULL * - size : [in] the size of the buffer in bytes * return value : allocated object */ SqOggResult * SqOggResultNewBuffer(SqOggResult * head, int size) { SqOggResult * object= (SqOggResult *) malloc(sizeof(SqOggResult)); object->buffer= (char *) malloc(sizeof(char) * size); object->size= size; object->next= NULL; if (head != NULL) { SqOggResult * tail; for (tail= head; tail->next != NULL; tail= tail->next); tail->next= object; } return object; } /* Allocate new buffer for result. * - size : [in] size of required buffer */ void * requestBuffer(SqOgg * ogg, size_t size) { SqOggResult * bufferNode= SqOggResultNewBuffer(ogg->resultList, size); if (ogg->resultList == NULL) { ogg->resultList= bufferNode; } return bufferNode->buffer; } /* Destroy list of buffers * - head : [in] top of the list */ void SqOggResultDestroy(SqOggResult * head) { if (head->next != NULL) { SqOggResultDestroy(head->next); head->next= NULL; } free(head->buffer); head->buffer= NULL; free(head); }; /* Copy all of the buffer. * The size of dest should be the result of SqOggReadSize() * - head : [in] top of the list * - dest : [out] flatten buffers * - size : [in] size of dest. * return value : written size */ int SqOggResultCopy(SqOggResult * head, char * dest, int size) { int pos= 0; SqOggResult * each; for (each= head; each != NULL; each= each->next) { int eachSize= each->size < (size - pos) ? each->size : (size - pos); memcpy(dest + pos, each->buffer, sizeof(char) * eachSize); pos= pos + eachSize; if (pos >= size) break; } return pos; }; /* write result buffer to the file handle (debug use) * - file : [in] file handle */ void SqOggResultWrite(SqOggPtr * oggp, FILE * file) { char * buf; int bytes; SqOgg * ogg= validate(oggp); if (ogg == NULL) return; bytes= SqOggReadSize(oggp); if (bytes == 0) return; buf= (char *) malloc(bytes); SqOggRead(oggp, buf, bytes); fwrite(buf, 1, bytes, file); } /************************************************************ * Low level Vorbis operation *************************************************************/ /* Process vorbis header * return value : SQ_OGG_SUCCESS if the header is complete * SQ_OGG_NEED_MORE if more data is needed (not error) * SQ_OGG_ERROR_HEADER if fatal error */ int vorbisDecodeHeader(SqOggPtr * oggp) { ogg_packet packet; int result; SqVorbis * v; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR_HEADER; v= ogg->vorbis; if (ogg->state == SQ_OGG_RUNNING) return SQ_OGG_SUCCESS; // memset(&packet, 0, sizeof(packet)); if (ogg->state == SQ_OGG_INITIALIZED) { if (SqOggPacketNext(oggp, &packet) == SQ_OGG_NEED_MORE) return SQ_OGG_NEED_MORE; result= vorbis_synthesis_headerin(&v->vinfo, &v->comment, &packet); if (result != 0) return SQ_OGG_ERROR_HEADER; ogg->state= SQ_VORBIS_GOT_INFO; } if (ogg->state == SQ_VORBIS_GOT_INFO) { if (SqOggPacketNext(oggp, &packet) == SQ_OGG_NEED_MORE) return SQ_OGG_NEED_MORE; result= vorbis_synthesis_headerin(&v->vinfo, &v->comment, &packet); if (result != 0) return SQ_OGG_ERROR_HEADER; ogg->state= SQ_VORBIS_GOT_COMMENT; } if (ogg->state == SQ_VORBIS_GOT_COMMENT) { if (SqOggPacketNext(oggp, &packet) == SQ_OGG_NEED_MORE) return SQ_OGG_NEED_MORE; result= vorbis_synthesis_headerin(&v->vinfo, &v->comment, &packet); if (result != 0) return SQ_OGG_ERROR_HEADER; ogg->channels= v->vinfo.channels; ogg->rate= v->vinfo.rate; vorbis_synthesis_init(&v->dsp_state, &v->vinfo); vorbis_block_init(&v->dsp_state,&v->block); ogg->state= SQ_OGG_RUNNING; return SQ_OGG_SUCCESS; } return SQ_OGG_NEED_MORE; } /* Get next pcm buffer * return value : SQ_OGG_SUCCESS if success * SQ_OGG_ERROR_HEADER if error in header process * SQ_OGG_ERROR_BODY if error in body process */ int vorbisDecode(SqOggPtr * oggp) { int channelsize; SqVorbis * v; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR /* UNDOCUMENTED */; v= ogg->vorbis; if (ogg->state != SQ_OGG_RUNNING) { int result= vorbisDecodeHeader(oggp); if (result == SQ_OGG_NEED_MORE) return SQ_OGG_SUCCESS; if (result == SQ_OGG_ERROR_HEADER) return SQ_OGG_ERROR_HEADER; } channelsize= v->vinfo.channels; while(1) { ogg_packet packet; if (SqOggPacketNext(oggp, &packet) == SQ_OGG_NEED_MORE) break; if(vorbis_synthesis(&v->block, &packet) == 0) { vorbis_synthesis_blockin(&v->dsp_state, &v->block); } else { fprintf(stderr, "ERROR!\n"); return SQ_OGG_ERROR_BODY; } while(1){ int pos; int channel; float **pcm; SqOggResult * bufferNode; ogg_int16_t * buffer; int samples= vorbis_synthesis_pcmout(&v->dsp_state, &pcm); if (samples <= 0) break; bufferNode= SqOggResultNewBuffer(ogg->resultList, sizeof(ogg_int16_t) * samples * channelsize); if (ogg->resultList == NULL) { ogg->resultList= bufferNode; } buffer= (ogg_int16_t *) bufferNode->buffer; /* convert floats to 16 bit signed ints little endian interleave */ for(channel= 0; channel < channelsize; channel++) { for(pos= 0; pos < samples; pos++) { int val= pcm[channel][pos] * 32767.f; /* might as well guard against clipping */ if (val > 32767) val= 32767; if (val < -32768) val= -32768; /* write in interleave platform endian */ buffer[pos * channelsize + channel]= val; } } vorbis_synthesis_read(&v->dsp_state, samples); } } return SQ_OGG_SUCCESS; } /* write the logical stream into the result list * - ogg : ogg state * - func :ogg_stream_flush or ogg_stream_pageout */ void outputEncoded(SqOgg * ogg, int (*func)(ogg_stream_state *os, ogg_page *og)) { while(1){ ogg_page page; int size; SqOggResult * bufferNode; int result= func(&ogg->stream_state, &page); if (result == 0) { break; } size= page.header_len + page.body_len; bufferNode= SqOggResultNewBuffer(ogg->resultList, size); if (ogg->resultList == NULL) { ogg->resultList= bufferNode; } memcpy(bufferNode->buffer, page.header, page.header_len); memcpy(bufferNode->buffer + page.header_len, page.body, page.body_len); } } /* Output vorbis header */ int vorbisEncodeHeader(SqOgg * ogg) { ogg_packet header; ogg_packet header_comm; ogg_packet header_code; { int result= vorbis_encode_init_vbr(&ogg->vorbis->vinfo, ogg->channels, ogg->rate, ogg->quality); if (result != 0) return SQ_OGG_ERROR_HEADER; vorbis_analysis_init(&ogg->vorbis->dsp_state, &ogg->vorbis->vinfo); } vorbis_comment_init(&ogg->vorbis->comment); vorbis_comment_add_tag(&ogg->vorbis->comment, "ENCODER", "Squeak"); /* write info and headers into three packets */ vorbis_analysis_headerout(&ogg->vorbis->dsp_state, &ogg->vorbis->comment, &header, &header_comm, &header_code); vorbis_comment_clear(&ogg->vorbis->comment); /* write three packets into the logical stream */ ogg_stream_packetin(&ogg->stream_state, &header); ogg_stream_packetin(&ogg->stream_state, &header_comm); ogg_stream_packetin(&ogg->stream_state, &header_code); outputEncoded(ogg, ogg_stream_flush); ogg->state= SQ_OGG_RUNNING; return SQ_OGG_SUCCESS; } /* Write encoded vorbis stream to packets */ void vorbisPacketWrite(SqOgg * ogg) { /* vorbis does some data preanalysis, then divvies up blocks for more involved (potentially parallel) processing. Get a single block for encoding now */ vorbis_block block; /* TODO!!!! */ vorbis_block_init(&ogg->vorbis->dsp_state, &block); while(vorbis_analysis_blockout(&ogg->vorbis->dsp_state, &block) == 1) { ogg_packet packet; /* one raw packet of data for decode */ vorbis_analysis(&block, NULL); vorbis_bitrate_addblock(&block); /* Write dsp state to a packet. */ while(vorbis_bitrate_flushpacket(&ogg->vorbis->dsp_state, &packet)) { /* Write a packet to logical stream */ ogg_stream_packetin(&ogg->stream_state, &packet); } /* Write logical stream to page */ outputEncoded(ogg, ogg_stream_pageout); } vorbis_block_clear(&block); } /* Encode vorbis body * - buffer : [in] sound data * - size : [in] buffer size in bytes */ int vorbisEncode(SqOggPtr * oggp, ogg_int16_t * readbuffer, int size) { int channelsize; int framesize; float ** buffer; long i, c; SqOgg * ogg= validate(oggp); if (ogg == NULL) return 0; if (ogg->state != SQ_OGG_RUNNING) { if (vorbisEncodeHeader(ogg) == SQ_OGG_ERROR_HEADER) return SQ_OGG_ERROR_HEADER; } channelsize= ogg->vorbis->vinfo.channels; framesize= size / (sizeof(ogg_int16_t) * channelsize); buffer= vorbis_analysis_buffer(&ogg->vorbis->dsp_state, framesize); /* platform endian uninterleave samples */ for(i = 0; i < framesize; i++) { for (c= 0; c < channelsize; c++) { buffer[c][i]= readbuffer[i * channelsize + c] /32768.f; } } vorbis_analysis_wrote(&ogg->vorbis->dsp_state, framesize); vorbisPacketWrite(ogg); return SQ_OGG_SUCCESS; } /* output end of stream */ void vorbisEOS(SqOgg * ogg) { vorbis_analysis_wrote(&ogg->vorbis->dsp_state, 0); vorbisPacketWrite(ogg); } /************************************************************ * Low level Speex interface *************************************************************/ /* Initialize Speex encoder */ SqSpeex * SqSpeexEncoder() { SqSpeex * speex; speex= (SqSpeex *) calloc(1, sizeof(SqSpeex)); speex->remainSize= 0; return speex; } /* Initialize Speex decoder from a packet * - packet : source packet * return value: a pointer of SqSpeex * : NULL if error */ SqSpeex * SqSpeexDecoder(ogg_packet * packet) { SqSpeex * speex; int modeID; SpeexMode * mode; SpeexHeader * header; SpeexState speexState; int channels; speex= (SqSpeex *) calloc(1, sizeof(SqSpeex)); header = speex_packet_to_header((char *) packet->packet, packet->bytes); if (!header) { return NULL; } modeID= header->mode; mode= speex_lib_get_mode(modeID); speexState= speex_decoder_init(mode); if (!speexState) { return NULL; } speex_decoder_ctl(speexState, SPEEX_SET_SAMPLING_RATE, &header->rate); channels= header->nb_channels; free(header); if (channels != 1) return NULL; /* only supports mono channel */ speex->state= speexState; speex_bits_init(&speex->bits); return speex; } /* Decode a chunk * speex : Speex decoder * buffer : compressed data * bytes : size of the buffer in bytes * return value : size of decoded sound in bytes */ int SqSpeexDecodeWrite(SqSpeex * speex, char * buffer, size_t bytes) { int frame_size; speex_bits_read_from(&speex->bits, buffer, bytes); speex_decoder_ctl(speex->state, SPEEX_GET_FRAME_SIZE, &frame_size); return frame_size * 2; } /* Get decoded sound, SqSpeexDecodeWrite() should be called before this. * speex : Speex decoder * buffer : sound buffer * bytes : size of the buffer in bytes */ void SqSpeexDecodeRead(SqSpeex * speex, char * buffer) { speex_decode_int(speex->state, &speex->bits, (short *) buffer); } /************************************************************ * Ogg Speex interface *************************************************************/ /* Decode Ogg Speex packet in the buffer */ int speexDecode(SqOggPtr * oggp) { ogg_packet packet; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR; while (1) { if (SqOggPacketNext(oggp, &packet) == SQ_OGG_NEED_MORE) return SQ_OGG_SUCCESS; if (ogg->state == SQ_OGG_INITIALIZED) { ogg->speex= SqSpeexDecoder(&packet); if (ogg->speex == NULL) return SQ_OGG_ERROR_HEADER; speex_decoder_ctl(ogg->speex->state, SPEEX_GET_SAMPLING_RATE, &ogg->rate); ogg->channels= 1; /* now only supports mono channel */ ogg->state= SQ_SPEEX_GOT_INFO; } else if (ogg->state == SQ_SPEEX_GOT_INFO) { /* process comment (but not implemented yet */ ogg->state= SQ_OGG_RUNNING; } else if (ogg->state == SQ_OGG_RUNNING) { short * decompressed; int sound_size= SqSpeexDecodeWrite(ogg->speex, (char *) packet.packet, packet.bytes); decompressed = (short *) requestBuffer(ogg, sound_size); SqSpeexDecodeRead(ogg->speex, (char *) decompressed); } } } /* Build dummy comment structure. * Now this library doesn't support comment in speex. * It genarates just a placeholder for the second packet. * It was copied from speexenc.c in speex-1.0.5 dist. */ #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \ buf[base+2]=((val)>>16)&0xff; \ buf[base+1]=((val)>>8)&0xff; \ buf[base]=(val)&0xff; \ }while(0) void comment_init(char **comments, int* length, char *vendor_string) { int vendor_length=strlen(vendor_string); int user_comment_list_length=0; int len=4+vendor_length+4; char *p=(char*)malloc(len); if(p==NULL){ } writeint(p, 0, vendor_length); memcpy(p+4, vendor_string, vendor_length); writeint(p, 4+vendor_length, user_comment_list_length); *length=len; *comments=p; } #undef writeint void speexEncodeHeader(SqOggPtr * oggp) { SpeexHeader header; SpeexMode *mode= NULL; int modeID= SPEEX_MODEID_WB; int nframes= 1; ogg_packet packet; int rate; int nb_channels; int quality; SqOgg * ogg= validate(oggp); if (ogg == NULL) return; rate= ogg->rate; nb_channels= ogg->channels; quality= ogg->quality; if (rate > 25000) { modeID = SPEEX_MODEID_UWB; } else if (rate > 12500) { modeID = SPEEX_MODEID_WB; } else { modeID = SPEEX_MODEID_NB; } mode = speex_lib_get_mode (modeID); speex_init_header(&header, rate, nb_channels, mode); header.frames_per_packet= nframes; header.vbr= 0; header.nb_channels= nb_channels; packet.packet = (unsigned char *) speex_header_to_packet(&header, (int*) &(packet.bytes)); packet.b_o_s = 1; packet.e_o_s = 0; packet.granulepos = 0; packet.packetno = 0; SqOggPacketWrite(oggp, &packet); free(packet.packet); ogg->speex->state = speex_encoder_init(mode); speex_encoder_ctl(ogg->speex->state, SPEEX_SET_SAMPLING_RATE, &rate); speex_encoder_ctl(ogg->speex->state, SPEEX_SET_QUALITY, &quality); speex_bits_init(&ogg->speex->bits); { char *comments; int comments_length; char *vendor_string = "Encoded with Squeak"; comment_init(&comments, &comments_length, vendor_string); packet.packet = (unsigned char *)comments; packet.bytes = comments_length; packet.b_o_s = 0; packet.e_o_s = 0; packet.granulepos = 0; packet.packetno = 1; } SqOggPacketWrite(oggp, &packet); SqOggPacketFlush(oggp); ogg->state= SQ_OGG_RUNNING; } /* Encode Ogg Speex packet in the buffer * - buffer : [in] sound data * - bytes : [in] size of the data in bytes */ int speexEncode(SqOggPtr * oggp, const char * buffer, size_t bytes) { int frameSize; ogg_int16_t * source; int totalFrame; int written= 0; /* written sound size in frames */ SqSpeex * spx; ogg_packet packet; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR; spx= ogg->speex; if (ogg->state != SQ_OGG_RUNNING) speexEncodeHeader(oggp); /* Copy given sound + remain sound to working buffer */ totalFrame= bytes / sizeof(ogg_int16_t) + spx->remainSize; source= (ogg_int16_t *) malloc(sizeof(ogg_int16_t) * totalFrame); memcpy(source, spx->remain, sizeof(ogg_int16_t) * spx->remainSize); memcpy(source + spx->remainSize, buffer, bytes); speex_encoder_ctl(spx->state, SPEEX_GET_FRAME_SIZE, &frameSize); /* Send each frame */ for (written= 0; totalFrame - written >= frameSize; written += frameSize) { int nbBytes; unsigned char * encoded; speex_bits_reset(&spx->bits); speex_encode_int(spx->state, source + written, &spx->bits); nbBytes= speex_bits_nbytes(&spx->bits); encoded = (unsigned char *)malloc(nbBytes); // speex_bits_insert_terminator(&spx->bits); speex_bits_write(&spx->bits, (char *) encoded, nbBytes); /* write data */ packet.packet= encoded; packet.bytes= nbBytes; packet.e_o_s= 0; SqOggPacketWrite(oggp, &packet); free(encoded); } spx->remainSize= totalFrame - written; memcpy(spx->remain, source + written, sizeof(ogg_int16_t) * spx->remainSize); return SQ_OGG_SUCCESS; } /************************************************************ * Low level Ogg interface *************************************************************/ void SqOggPacketWrite(SqOggPtr * oggp, ogg_packet * packet) { SqOgg * ogg= validate(oggp); if (ogg == NULL) return; if (ogg->packetStatus == SQ_STREAM_UNINITIALIZED) { packet->b_o_s= 1; ogg->packetStatus= SQ_STREAM_BEGAN; } ogg_stream_packetin(&ogg->stream_state, packet); outputEncoded(ogg, ogg_stream_pageout); } /* Write end of stream packet */ void SqOggPacketWriteEOS(SqOggPtr * oggp) { ogg_packet packet; SqOgg * ogg= validate(oggp); if (ogg == NULL) return; packet.packet = NULL; packet.bytes = 0; packet.b_o_s = 0; packet.e_o_s = 1; SqOggPacketWrite(oggp, &packet); } void SqOggPacketFlush(SqOggPtr * oggp) { SqOgg * ogg= validate(oggp); if (ogg == NULL) return; outputEncoded(ogg, ogg_stream_flush); } /************************************************************ * Basic Ogg Vorbis / Speex buffer operations *************************************************************/ /* Initialize vorbis encoder */ SqVorbis * initVorbis() { SqVorbis * vorbis= (SqVorbis *) calloc(1, sizeof(SqVorbis)); vorbis_info_init(&vorbis->vinfo); vorbis_comment_init(&vorbis->comment); return vorbis; } /* Create new instance of decoder * mode - [in] open mode * oggp - [out] built object pointer * return value: an initialized struct */ void SqOggOpen(int mode, SqOggPtr * oggp) { SqOgg * ogg = (SqOgg *) calloc(1, sizeof(SqOgg)); ogg_sync_init(&ogg->sync_state); ogg->packetStatus= SQ_STREAM_UNINITIALIZED; ogg->state= SQ_OGG_INITIALIZED; ogg->resultList= NULL; ogg->mode= mode; if (mode & SQ_OGG_ENCODE) { ogg_stream_init(&ogg->stream_state, 0x999); /* initialize logical stream */ } if (ogg->mode == (SQ_SPEEX | SQ_OGG_ENCODE)) { ogg->speex= SqSpeexEncoder(); ogg->rate= 16000; ogg->channels= 1; ogg->quality= 8; } if (ogg->mode & SQ_VORBIS) { ogg->vorbis= initVorbis(); ogg->rate= 22050; ogg->channels= 1; ogg->quality= 0.1; } if (! thisSession) { thisSession= time(NULL); } oggp->sessionID= thisSession; oggp->ogg= ogg; } /* Encode or Decode process * - buffer : [in] data * - bytes : [in] size of the data * return value : SQ_OGG_SUCCESS if success * SQ_OGG_ERROR_HEADER if error in header process * SQ_OGG_ERROR_BODY if error in body process */ int SqOggWrite(SqOggPtr * oggp, const char * buffer, size_t bytes) { int result= SQ_OGG_SUCCESS; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR; if (ogg->mode == (SQ_VORBIS | SQ_OGG_ENCODE)) { result= vorbisEncode(oggp, (ogg_int16_t *) buffer, bytes); } else if (ogg->mode == (SQ_SPEEX | SQ_OGG_ENCODE)) { result= speexEncode(oggp, buffer, bytes); } else if (ogg->mode == (SQ_VORBIS | SQ_OGG_DECODE)) { writeBuffer(ogg, buffer, bytes); result= vorbisDecode(oggp); } else if (ogg->mode == (SQ_SPEEX | SQ_OGG_DECODE)) { writeBuffer(ogg, buffer, bytes); result= speexDecode(oggp); } else { writeBuffer(ogg, buffer, bytes); /* It is also used in test/packet.c */ } return result; } /* Get the size of result buffer * return value : size */ size_t SqOggReadSize(SqOggPtr * oggp) { SqOgg * ogg= validate(oggp); if (ogg == NULL) return 0; return SqOggResultSize(ogg->resultList); } /* Get flatten size of the buffers * - head : [in] top of the list * return value : size */ int SqOggResultSize(SqOggResult * head) { SqOggResult * each; int size= 0; for (each= head; each != NULL; each= each->next) { size= size + each->size; } return size; }; /* Read the result, and cleanup buffer. * - dest : buffer to store the result * - bytes : size of the result buffer * return value: actually written size (in bytes) */ size_t SqOggRead(SqOggPtr * oggp, char * dest, size_t bytes) { int result; SqOgg * ogg= validate(oggp); if (ogg == NULL) return 0; if (ogg->resultList == NULL) return 0; result= SqOggResultCopy(ogg->resultList, dest, bytes); SqOggResultDestroy(ogg->resultList); ogg->resultList= NULL; return result; } /* Write End Of Stream packet. */ void SqOggWriteEOS(SqOggPtr * oggp) { SqOgg * ogg= validate(oggp); if (ogg == NULL) return; if (ogg->mode & SQ_VORBIS) { vorbisEOS(ogg); } else { SqOggPacketWriteEOS(oggp); } } /* Destroy the instance */ void SqOggClose(SqOggPtr * oggp) { SqOgg * ogg= validate(oggp); if (ogg == NULL) return; ogg_sync_clear(&ogg->sync_state); if (ogg->mode & SQ_SPEEX) free(ogg->speex); if (ogg->mode & SQ_VORBIS) { vorbis_info_clear(&ogg->vorbis->vinfo); vorbis_comment_clear(&ogg->vorbis->comment); free(ogg->vorbis); } oggp->sessionID= 0; oggp->ogg= NULL; free(ogg); } /************************************************************ * Accessors *************************************************************/ /* Set size of channels */ void SqOggSetChannels(SqOggPtr * oggp, int channels) { if(validate(oggp) == NULL) return; oggp->ogg->channels= channels; } /* Set rate */ void SqOggSetRate(SqOggPtr * oggp, int rate) { if(validate(oggp) == NULL) return; oggp->ogg->rate= rate; } /* Set quality */ void SqOggSetQuality(SqOggPtr * oggp, float quality) { if(validate(oggp) == NULL) return; oggp->ogg->quality= quality; } /* Get rate */ int SqOggGetRate(SqOggPtr * oggp) { if(validate(oggp) == NULL) return SQ_OGG_ERROR; return oggp->ogg->rate; } /* Get size of channles */ int SqOggGetChannels(SqOggPtr * oggp) { if(validate(oggp) == NULL) return SQ_OGG_ERROR; return oggp->ogg->channels; } /* Get vendor name */ int SqOggGetVendor(SqOggPtr * oggp, char * dest, size_t size) { SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR; if (!(ogg->mode & SQ_VORBIS)) return SQ_OGG_ERROR; if (ogg->state != SQ_OGG_RUNNING) return SQ_OGG_NEED_MORE; strncpy(dest, ogg->vorbis->comment.vendor, size); return SQ_OGG_SUCCESS; } /* Answer comments */ int SqOggGetComment(SqOggPtr * oggp, char * dest, size_t size) { int pos= 0; char **ptr; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR; if (!(ogg->mode & SQ_VORBIS)) return SQ_OGG_ERROR; if (ogg->state != SQ_OGG_RUNNING) return SQ_OGG_NEED_MORE; ptr= ogg->vorbis->comment.user_comments; while(*ptr){ int eachSize= strlen(*ptr) + 1; eachSize= (eachSize < size - pos) ? eachSize : (size - pos); strncpy(dest + pos, *ptr, eachSize); pos= pos + eachSize; if (pos >= size) return SQ_OGG_SUCCESS; ++ptr; } return SQ_OGG_SUCCESS; } /* Get length of comments */ int SqOggGetCommentSize(SqOggPtr * oggp) { int pos= 0; char **ptr; SqOgg * ogg= validate(oggp); if (ogg == NULL) return SQ_OGG_ERROR; if (!(ogg->mode & SQ_VORBIS)) return SQ_OGG_ERROR; if (ogg->state != SQ_OGG_RUNNING) return SQ_OGG_NEED_MORE; ptr=ogg->vorbis->comment.user_comments; while(*ptr){ pos= pos + strlen(*ptr) + 1; ++ptr; } return pos; } /* Answer pcm state */ int SqOggGetState(SqOggPtr * oggp) { if(validate(oggp) == NULL) return SQ_OGG_ERROR; return oggp->ogg->state; }