123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287 |
- /* Copyright (C)2002-2017 Jean-Marc Valin
- Copyright (C)2007-2013 Xiph.Org Foundation
- Copyright (C)2008-2013 Gregory Maxwell
- File: opusenc.c
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- - Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- - Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <stdarg.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <assert.h>
- #include "opusenc.h"
- #include "opus_header.h"
- #include "speex_resampler.h"
- #include "picture.h"
- #include "ogg_packer.h"
- #include "unicode_support.h"
- /* Bump this when we change the ABI. */
- #define OPE_ABI_VERSION 0
- #define LPC_PADDING 120
- #define LPC_ORDER 24
- #define LPC_INPUT 480
- /* Make the following constant always equal to 2*cos(M_PI/LPC_PADDING) */
- #define LPC_GOERTZEL_CONST 1.99931465f
- /* Allow up to 2 seconds for delayed decision. */
- #define MAX_LOOKAHEAD 96000
- /* We can't have a circular buffer (because of delayed decision), so let's not copy too often. */
- #define BUFFER_EXTRA 24000
- #define BUFFER_SAMPLES (MAX_LOOKAHEAD + BUFFER_EXTRA)
- #define MIN(a,b) ((a) < (b) ? (a) : (b))
- #define MAX(a,b) ((a) > (b) ? (a) : (b))
- #ifdef _MSC_VER
- # if (_MSC_VER < 1900)
- # define snprintf _snprintf
- # endif
- #endif
- struct StdioObject {
- FILE *file;
- };
- struct OggOpusComments {
- char *comment;
- int comment_length;
- int seen_file_icons;
- };
- /* Create a new comments object. The vendor string is optional. */
- OggOpusComments *ope_comments_create() {
- OggOpusComments *c;
- const char *libopus_str;
- char vendor_str[1024];
- c = malloc(sizeof(*c));
- if (c == NULL) return NULL;
- libopus_str = opus_get_version_string();
- snprintf(vendor_str, sizeof(vendor_str), "%s, %s %s", libopus_str, PACKAGE_NAME, PACKAGE_VERSION);
- opeint_comment_init(&c->comment, &c->comment_length, vendor_str);
- c->seen_file_icons = 0;
- if (c->comment == NULL) {
- free(c);
- return NULL;
- } else {
- return c;
- }
- }
- /* Create a deep copy of a comments object. */
- OggOpusComments *ope_comments_copy(OggOpusComments *comments) {
- OggOpusComments *c;
- c = malloc(sizeof(*c));
- if (c == NULL) return NULL;
- memcpy(c, comments, sizeof(*c));
- c->comment = malloc(comments->comment_length);
- if (c->comment == NULL) {
- free(c);
- return NULL;
- } else {
- memcpy(c->comment, comments->comment, comments->comment_length);
- return c;
- }
- }
- /* Destroys a comments object. */
- void ope_comments_destroy(OggOpusComments *comments){
- free(comments->comment);
- free(comments);
- }
- /* Add a comment. */
- int ope_comments_add(OggOpusComments *comments, const char *tag, const char *val) {
- if (tag == NULL || val == NULL) return OPE_BAD_ARG;
- if (strchr(tag, '=')) return OPE_BAD_ARG;
- if (opeint_comment_add(&comments->comment, &comments->comment_length, tag, val)) return OPE_ALLOC_FAIL;
- return OPE_OK;
- }
- /* Add a comment. */
- int ope_comments_add_string(OggOpusComments *comments, const char *tag_and_val) {
- if (!strchr(tag_and_val, '=')) return OPE_BAD_ARG;
- if (opeint_comment_add(&comments->comment, &comments->comment_length, NULL, tag_and_val)) return OPE_ALLOC_FAIL;
- return OPE_OK;
- }
- int ope_comments_add_picture(OggOpusComments *comments, const char *filename, int picture_type, const char *description) {
- char *picture_data;
- int err;
- picture_data = opeint_parse_picture_specification(filename, picture_type, description, &err, &comments->seen_file_icons);
- if (picture_data == NULL || err != OPE_OK){
- return err;
- }
- opeint_comment_add(&comments->comment, &comments->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
- free(picture_data);
- return OPE_OK;
- }
- int ope_comments_add_picture_from_memory(OggOpusComments *comments, const char *ptr, size_t size, int picture_type, const char *description) {
- char *picture_data;
- int err;
- picture_data = opeint_parse_picture_specification_from_memory(ptr, size, picture_type, description, &err, &comments->seen_file_icons);
- if (picture_data == NULL || err != OPE_OK){
- return err;
- }
- opeint_comment_add(&comments->comment, &comments->comment_length, "METADATA_BLOCK_PICTURE", picture_data);
- free(picture_data);
- return OPE_OK;
- }
- typedef struct EncStream EncStream;
- struct EncStream {
- void *user_data;
- int serialno_is_set;
- int serialno;
- int stream_is_init;
- int packetno;
- char *comment;
- int comment_length;
- int seen_file_icons;
- int close_at_end;
- int header_is_frozen;
- opus_int64 end_granule;
- opus_int64 granule_offset;
- EncStream *next;
- };
- int opeint_use_projection(int channel_mapping) {
- if (channel_mapping==3){
- return 1;
- }
- return 0;
- }
- int opeint_encoder_surround_init(
- OpusGenericEncoder *st, int Fs, int channels, int channel_mapping,
- int *nb_streams, int *nb_coupled, unsigned char *stream_map, int application) {
- int ret;
- #ifdef OPUS_HAVE_OPUS_PROJECTION_H
- if(opeint_use_projection(channel_mapping)){
- int ci;
- st->pr=opus_projection_ambisonics_encoder_create(Fs, channels,
- channel_mapping, nb_streams, nb_coupled, application, &ret);
- for (ci = 0; ci < channels; ci++) {
- stream_map[ci] = ci;
- }
- st->ms=NULL;
- }
- else
- #endif
- {
- #ifdef OPUS_HAVE_OPUS_PROJECTION_H
- st->pr=NULL;
- #endif
- st->ms=opus_multistream_surround_encoder_create(Fs, channels,
- channel_mapping, nb_streams, nb_coupled, stream_map, application, &ret);
- }
- return ret;
- }
- void opeint_encoder_cleanup(OpusGenericEncoder *st) {
- #ifdef OPUS_HAVE_OPUS_PROJECTION_H
- if (st->pr) opus_projection_encoder_destroy(st->pr);
- #endif
- if (st->ms) opus_multistream_encoder_destroy(st->ms);
- }
- int opeint_encoder_init(
- OpusGenericEncoder *st, opus_int32 Fs, int channels, int streams,
- int coupled_streams, const unsigned char *mapping, int application) {
- int ret;
- #ifdef OPUS_HAVE_OPUS_PROJECTION_H
- st->pr=NULL;
- #endif
- st->ms=opus_multistream_encoder_create(Fs, channels, streams,
- coupled_streams, mapping, application, &ret);
- return ret;
- }
- int opeint_encode_float(
- OpusGenericEncoder *st,
- const float *pcm,
- int frame_size,
- unsigned char *data,
- opus_int32 max_data_bytes) {
- int ret;
- #ifdef OPUS_HAVE_OPUS_PROJECTION_H
- if (st->pr) ret=opus_projection_encode_float(st->pr, pcm, frame_size, data, max_data_bytes);
- else
- #endif
- ret=opus_multistream_encode_float(st->ms, pcm, frame_size, data, max_data_bytes);
- return ret;
- }
- struct OggOpusEnc {
- OpusGenericEncoder st;
- oggpacker *oggp;
- int unrecoverable;
- int pull_api;
- int rate;
- int channels;
- float *buffer;
- int buffer_start;
- int buffer_end;
- SpeexResamplerState *re;
- int frame_size;
- int decision_delay;
- int max_ogg_delay;
- int global_granule_offset;
- opus_int64 curr_granule;
- opus_int64 write_granule;
- opus_int64 last_page_granule;
- int draining;
- int frame_size_request;
- float *lpc_buffer;
- unsigned char *chaining_keyframe;
- int chaining_keyframe_length;
- OpusEncCallbacks callbacks;
- ope_packet_func packet_callback;
- void *packet_callback_data;
- OpusHeader header;
- int comment_padding;
- EncStream *streams;
- EncStream *last_stream;
- };
- static int output_pages(OggOpusEnc *enc) {
- unsigned char *page;
- int len;
- while (oggp_get_next_page(enc->oggp, &page, &len)) {
- int ret = enc->callbacks.write(enc->streams->user_data, page, len);
- if (ret) return ret;
- }
- return 0;
- }
- static int oe_flush_page(OggOpusEnc *enc) {
- oggp_flush_page(enc->oggp);
- if (!enc->pull_api) return output_pages(enc);
- return 0;
- }
- static int stdio_write(void *user_data, const unsigned char *ptr, opus_int32 len) {
- int ret;
- struct StdioObject *obj = (struct StdioObject*)user_data;
- ret = fwrite(ptr, 1, len, obj->file) != (size_t)len;
- return ret;
- }
- static int stdio_close(void *user_data) {
- struct StdioObject *obj = (struct StdioObject*)user_data;
- int ret = 0;
- if (obj->file) ret = fclose(obj->file);
- free(obj);
- return ret!=0;
- }
- static const OpusEncCallbacks stdio_callbacks = {
- stdio_write,
- stdio_close
- };
- /* Create a new OggOpus file. */
- OggOpusEnc *ope_encoder_create_file(const char *path, OggOpusComments *comments, opus_int32 rate, int channels, int family, int *error) {
- OggOpusEnc *enc;
- struct StdioObject *obj;
- obj = malloc(sizeof(*obj));
- if (obj == NULL) {
- if (error) *error = OPE_ALLOC_FAIL;
- return NULL;
- }
- enc = ope_encoder_create_callbacks(&stdio_callbacks, obj, comments, rate, channels, family, error);
- if (enc == NULL || (error && *error)) {
- free(obj);
- return NULL;
- }
- obj->file = opeint_fopen(path, "wb");
- if (!obj->file) {
- if (error) *error = OPE_CANNOT_OPEN;
- ope_encoder_destroy(enc);
- return NULL;
- }
- return enc;
- }
- EncStream *stream_create(OggOpusComments *comments) {
- EncStream *stream;
- stream = malloc(sizeof(*stream));
- if (!stream) return NULL;
- stream->next = NULL;
- stream->close_at_end = 1;
- stream->serialno_is_set = 0;
- stream->stream_is_init = 0;
- stream->header_is_frozen = 0;
- stream->granule_offset = 0;
- stream->comment = malloc(comments->comment_length);
- if (stream->comment == NULL) goto fail;
- memcpy(stream->comment, comments->comment, comments->comment_length);
- stream->comment_length = comments->comment_length;
- stream->seen_file_icons = comments->seen_file_icons;
- return stream;
- fail:
- if (stream->comment) free(stream->comment);
- free(stream);
- return NULL;
- }
- static void stream_destroy(EncStream *stream) {
- if (stream->comment) free(stream->comment);
- free(stream);
- }
- /* Create a new OggOpus file (callback-based). */
- OggOpusEnc *ope_encoder_create_callbacks(const OpusEncCallbacks *callbacks, void *user_data,
- OggOpusComments *comments, opus_int32 rate, int channels, int family, int *error) {
- OggOpusEnc *enc=NULL;
- int ret;
- if (family != 0 && family != 1 &&
- #ifdef OPUS_HAVE_OPUS_PROJECTION_H
- family != 2 && family != 3 &&
- #endif
- family != 255 && family != -1) {
- if (error) {
- if (family < -1 || family > 255) *error = OPE_BAD_ARG;
- else *error = OPE_UNIMPLEMENTED;
- }
- return NULL;
- }
- if (channels <= 0 || channels > 255) {
- if (error) *error = OPE_BAD_ARG;
- return NULL;
- }
- if (rate <= 0) {
- if (error) *error = OPE_BAD_ARG;
- return NULL;
- }
- /* Setting the most common failure up-front. */
- if (error) *error = OPE_ALLOC_FAIL;
- if ( (enc = malloc(sizeof(*enc))) == NULL) goto fail;
- enc->buffer = NULL;
- enc->lpc_buffer = NULL;
- if ( (enc->streams = stream_create(comments)) == NULL) goto fail;
- enc->last_stream = enc->streams;
- enc->oggp = NULL;
- /* Not initializing anything is an unrecoverable error. */
- enc->unrecoverable = family == -1 ? OPE_TOO_LATE : 0;
- enc->pull_api = 0;
- enc->packet_callback = NULL;
- enc->rate = rate;
- enc->channels = channels;
- enc->frame_size = 960;
- enc->decision_delay = 96000;
- enc->max_ogg_delay = 48000;
- enc->chaining_keyframe = NULL;
- enc->chaining_keyframe_length = -1;
- enc->comment_padding = 512;
- enc->header.channels=channels;
- enc->header.channel_mapping=family;
- enc->header.input_sample_rate=rate;
- enc->header.gain=0;
- if (family != -1) {
- ret=opeint_encoder_surround_init(&enc->st, 48000, channels,
- enc->header.channel_mapping, &enc->header.nb_streams,
- &enc->header.nb_coupled, enc->header.stream_map,
- OPUS_APPLICATION_AUDIO);
- if (! (ret == OPUS_OK) ) {
- if (ret == OPUS_BAD_ARG) ret = OPE_BAD_ARG;
- else if (ret == OPUS_INTERNAL_ERROR) ret = OPE_INTERNAL_ERROR;
- else if (ret == OPUS_UNIMPLEMENTED) ret = OPE_UNIMPLEMENTED;
- else if (ret == OPUS_ALLOC_FAIL) ret = OPE_ALLOC_FAIL;
- else ret = OPE_INTERNAL_ERROR;
- if (error) *error = ret;
- goto fail;
- }
- opeint_encoder_ctl(&enc->st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
- }
- if (rate != 48000) {
- enc->re = speex_resampler_init(channels, rate, 48000, 5, NULL);
- if (enc->re == NULL) goto fail;
- speex_resampler_skip_zeros(enc->re);
- } else {
- enc->re = NULL;
- }
- enc->global_granule_offset = -1;
- enc->curr_granule = 0;
- enc->write_granule = 0;
- enc->last_page_granule = 0;
- enc->draining = 0;
- if ( (enc->buffer = malloc(sizeof(*enc->buffer)*BUFFER_SAMPLES*channels)) == NULL) goto fail;
- if (rate != 48000) {
- /* Allocate an extra LPC_PADDING samples so we can do the padding in-place. */
- if ( (enc->lpc_buffer = malloc(sizeof(*enc->lpc_buffer)*(LPC_INPUT+LPC_PADDING)*channels)) == NULL) goto fail;
- memset(enc->lpc_buffer, 0, sizeof(*enc->lpc_buffer)*LPC_INPUT*channels);
- }
- enc->buffer_start = enc->buffer_end = 0;
- if (callbacks != NULL)
- {
- enc->callbacks = *callbacks;
- }
- enc->streams->user_data = user_data;
- if (error) *error = OPE_OK;
- return enc;
- fail:
- if (enc) {
- opeint_encoder_cleanup(&enc->st);
- if (enc->buffer) free(enc->buffer);
- if (enc->streams) stream_destroy(enc->streams);
- if (enc->lpc_buffer) free(enc->lpc_buffer);
- free(enc);
- }
- return NULL;
- }
- /* Create a new OggOpus stream, pulling one page at a time. */
- OggOpusEnc *ope_encoder_create_pull(OggOpusComments *comments, opus_int32 rate, int channels, int family, int *error) {
- OggOpusEnc *enc = ope_encoder_create_callbacks(NULL, NULL, comments, rate, channels, family, error);
- if (enc) enc->pull_api = 1;
- return enc;
- }
- int ope_encoder_deferred_init_with_mapping(OggOpusEnc *enc, int family, int streams,
- int coupled_streams, const unsigned char *mapping) {
- int ret;
- int i;
- if (family < 0 || family > 255) return OPE_BAD_ARG;
- else if (family != 1 &&
- #ifdef OPUS_HAVE_OPUS_PROJECTION_H
- family != 2 &&
- #endif
- family != 255) return OPE_UNIMPLEMENTED;
- else if (streams <= 0 || streams>255 || coupled_streams<0 || coupled_streams >= 128 || streams+coupled_streams > 255) return OPE_BAD_ARG;
- ret=opeint_encoder_init(&enc->st, 48000, enc->channels, streams, coupled_streams, mapping, OPUS_APPLICATION_AUDIO);
- if (! (ret == OPUS_OK) ) {
- if (ret == OPUS_BAD_ARG) ret = OPE_BAD_ARG;
- else if (ret == OPUS_INTERNAL_ERROR) ret = OPE_INTERNAL_ERROR;
- else if (ret == OPUS_UNIMPLEMENTED) ret = OPE_UNIMPLEMENTED;
- else if (ret == OPUS_ALLOC_FAIL) ret = OPE_ALLOC_FAIL;
- else ret = OPE_INTERNAL_ERROR;
- return ret;
- }
- opeint_encoder_ctl(&enc->st, OPUS_SET_EXPERT_FRAME_DURATION(OPUS_FRAMESIZE_20_MS));
- enc->unrecoverable = 0;
- enc->header.channel_mapping=family;
- enc->header.nb_streams = streams;
- enc->header.nb_coupled = coupled_streams;
- for (i=0;i<streams+coupled_streams;i++)
- enc->header.stream_map[i] = mapping[i];
- return OPE_OK;
- }
- static void init_stream(OggOpusEnc *enc) {
- assert(!enc->streams->stream_is_init);
- if (!enc->streams->serialno_is_set) {
- enc->streams->serialno = rand();
- }
- if (enc->oggp != NULL) oggp_chain(enc->oggp, enc->streams->serialno);
- else {
- enc->oggp = oggp_create(enc->streams->serialno);
- if (enc->oggp == NULL) {
- enc->unrecoverable = OPE_ALLOC_FAIL;
- return;
- }
- oggp_set_muxing_delay(enc->oggp, enc->max_ogg_delay);
- }
- opeint_comment_pad(&enc->streams->comment, &enc->streams->comment_length, enc->comment_padding);
- /* Get preskip at the last minute (when it can no longer change). */
- if (enc->global_granule_offset == -1) {
- opus_int32 tmp;
- int ret;
- ret=opeint_encoder_ctl(&enc->st, OPUS_GET_LOOKAHEAD(&tmp));
- if (ret == OPUS_OK) enc->header.preskip = tmp;
- else enc->header.preskip = 0;
- enc->global_granule_offset = enc->header.preskip;
- }
- /*Write header*/
- {
- int header_size;
- int ret;
- int packet_size;
- unsigned char *p;
- header_size = opeint_opus_header_get_size(&enc->header);
- p = oggp_get_packet_buffer(enc->oggp, header_size);
- packet_size = opeint_opus_header_to_packet(&enc->header, p, header_size, &enc->st);
- if (enc->packet_callback) enc->packet_callback(enc->packet_callback_data, p, packet_size, 0);
- oggp_commit_packet(enc->oggp, packet_size, 0, 0);
- ret = oe_flush_page(enc);
- if (ret) {
- enc->unrecoverable = OPE_WRITE_FAIL;
- return;
- }
- p = oggp_get_packet_buffer(enc->oggp, enc->streams->comment_length);
- memcpy(p, enc->streams->comment, enc->streams->comment_length);
- if (enc->packet_callback) enc->packet_callback(enc->packet_callback_data, p, enc->streams->comment_length, 0);
- oggp_commit_packet(enc->oggp, enc->streams->comment_length, 0, 0);
- ret = oe_flush_page(enc);
- if (ret) {
- enc->unrecoverable = OPE_WRITE_FAIL;
- return;
- }
- }
- enc->streams->stream_is_init = 1;
- enc->streams->packetno = 2;
- }
- static void shift_buffer(OggOpusEnc *enc) {
- /* Leaving enough in the buffer to do LPC extension if needed. */
- if (enc->buffer_start > LPC_INPUT) {
- memmove(&enc->buffer[0], &enc->buffer[enc->channels*(enc->buffer_start-LPC_INPUT)],
- enc->channels*(enc->buffer_end-enc->buffer_start+LPC_INPUT)*sizeof(*enc->buffer));
- enc->buffer_end -= enc->buffer_start-LPC_INPUT;
- enc->buffer_start = LPC_INPUT;
- }
- }
- static int compute_frame_samples(int size_request) {
- if (size_request <= OPUS_FRAMESIZE_40_MS) return 120<<(size_request-OPUS_FRAMESIZE_2_5_MS);
- else return (size_request-OPUS_FRAMESIZE_2_5_MS-2)*960;
- }
- static void encode_buffer(OggOpusEnc *enc) {
- opus_int32 max_packet_size;
- /* Round up when converting the granule pos because the decoder will round down. */
- opus_int64 end_granule48k = (enc->streams->end_granule*48000 + enc->rate - 1)/enc->rate + enc->global_granule_offset;
- max_packet_size = (1277*6+2)*enc->header.nb_streams;
- while (enc->buffer_end-enc->buffer_start > enc->frame_size + enc->decision_delay) {
- int cont;
- int e_o_s;
- opus_int32 pred;
- int nbBytes;
- unsigned char *packet;
- unsigned char *packet_copy = NULL;
- int is_keyframe=0;
- if (enc->unrecoverable) return;
- opeint_encoder_ctl(&enc->st, OPUS_GET_PREDICTION_DISABLED(&pred));
- /* FIXME: a frame that follows a keyframe generally doesn't need to be a keyframe
- unless there's two consecutive stream boundaries. */
- if (enc->curr_granule + 2*enc->frame_size>= end_granule48k && enc->streams->next) {
- opeint_encoder_ctl(&enc->st, OPUS_SET_PREDICTION_DISABLED(1));
- is_keyframe = 1;
- }
- /* Handle the last packet by making sure not to encode too much padding. */
- if (enc->curr_granule+enc->frame_size >= end_granule48k && enc->draining && enc->frame_size_request > OPUS_FRAMESIZE_20_MS) {
- int min_samples;
- int frame_size_request = OPUS_FRAMESIZE_20_MS;
- /* Minimum frame size required for the current frame to still meet the e_o_s condition. */
- min_samples = end_granule48k - enc->curr_granule;
- while (compute_frame_samples(frame_size_request) < min_samples) frame_size_request++;
- assert(frame_size_request <= enc->frame_size_request);
- ope_encoder_ctl(enc, OPUS_SET_EXPERT_FRAME_DURATION(frame_size_request));
- }
- packet = oggp_get_packet_buffer(enc->oggp, max_packet_size);
- nbBytes = opeint_encode_float(&enc->st, &enc->buffer[enc->channels*enc->buffer_start],
- enc->buffer_end-enc->buffer_start, packet, max_packet_size);
- if (nbBytes < 0) {
- /* Anything better we can do here? */
- enc->unrecoverable = OPE_INTERNAL_ERROR;
- return;
- }
- opeint_encoder_ctl(&enc->st, OPUS_SET_PREDICTION_DISABLED(pred));
- assert(nbBytes > 0);
- enc->curr_granule += enc->frame_size;
- do {
- int ret;
- opus_int64 granulepos;
- granulepos=enc->curr_granule-enc->streams->granule_offset;
- e_o_s=enc->curr_granule >= end_granule48k;
- cont = 0;
- if (e_o_s) granulepos=end_granule48k-enc->streams->granule_offset;
- if (packet_copy != NULL) {
- packet = oggp_get_packet_buffer(enc->oggp, max_packet_size);
- memcpy(packet, packet_copy, nbBytes);
- }
- if (enc->packet_callback) enc->packet_callback(enc->packet_callback_data, packet, nbBytes, 0);
- if ((e_o_s || is_keyframe) && packet_copy == NULL) {
- packet_copy = malloc(nbBytes);
- if (packet_copy == NULL) {
- /* Can't recover from allocation failing here. */
- enc->unrecoverable = OPE_ALLOC_FAIL;
- return;
- }
- memcpy(packet_copy, packet, nbBytes);
- }
- oggp_commit_packet(enc->oggp, nbBytes, granulepos, e_o_s);
- if (e_o_s) ret = oe_flush_page(enc);
- else if (!enc->pull_api) ret = output_pages(enc);
- else ret = 0;
- if (ret) {
- enc->unrecoverable = OPE_WRITE_FAIL;
- if (packet_copy) free(packet_copy);
- return;
- }
- if (e_o_s) {
- EncStream *tmp;
- tmp = enc->streams->next;
- if (enc->streams->close_at_end && !enc->pull_api) {
- ret = enc->callbacks.close(enc->streams->user_data);
- if (ret) {
- enc->unrecoverable = OPE_CLOSE_FAIL;
- free(packet_copy);
- return;
- }
- }
- stream_destroy(enc->streams);
- enc->streams = tmp;
- if (!tmp) enc->last_stream = NULL;
- if (enc->last_stream == NULL) {
- free(packet_copy);
- return;
- }
- /* We're done with this stream, start the next one. */
- enc->header.preskip = end_granule48k + enc->frame_size - enc->curr_granule;
- enc->streams->granule_offset = enc->curr_granule - enc->frame_size;
- if (enc->chaining_keyframe) {
- enc->header.preskip += enc->frame_size;
- enc->streams->granule_offset -= enc->frame_size;
- }
- init_stream(enc);
- if (enc->chaining_keyframe) {
- unsigned char *p;
- opus_int64 granulepos2=enc->curr_granule - enc->streams->granule_offset - enc->frame_size;
- p = oggp_get_packet_buffer(enc->oggp, enc->chaining_keyframe_length);
- memcpy(p, enc->chaining_keyframe, enc->chaining_keyframe_length);
- if (enc->packet_callback) enc->packet_callback(enc->packet_callback_data, enc->chaining_keyframe, enc->chaining_keyframe_length, 0);
- oggp_commit_packet(enc->oggp, enc->chaining_keyframe_length, granulepos2, 0);
- }
- end_granule48k = (enc->streams->end_granule*48000 + enc->rate - 1)/enc->rate + enc->global_granule_offset;
- cont = 1;
- }
- } while (cont);
- if (enc->chaining_keyframe) free(enc->chaining_keyframe);
- if (is_keyframe) {
- enc->chaining_keyframe_length = nbBytes;
- enc->chaining_keyframe = packet_copy;
- packet_copy = NULL;
- } else {
- enc->chaining_keyframe = NULL;
- enc->chaining_keyframe_length = -1;
- }
- if (packet_copy) free(packet_copy);
- enc->buffer_start += enc->frame_size;
- }
- /* If we've reached the end of the buffer, move everything back to the front. */
- if (enc->buffer_end == BUFFER_SAMPLES) {
- shift_buffer(enc);
- }
- /* This function must never leave the buffer full. */
- assert(enc->buffer_end < BUFFER_SAMPLES);
- }
- /* Add/encode any number of float samples to the file. */
- int ope_encoder_write_float(OggOpusEnc *enc, const float *pcm, int samples_per_channel) {
- int channels = enc->channels;
- if (enc->unrecoverable) return enc->unrecoverable;
- enc->last_stream->header_is_frozen = 1;
- if (!enc->streams->stream_is_init) init_stream(enc);
- if (samples_per_channel < 0) return OPE_BAD_ARG;
- enc->write_granule += samples_per_channel;
- enc->last_stream->end_granule = enc->write_granule;
- if (enc->lpc_buffer) {
- int i;
- if (samples_per_channel < LPC_INPUT) {
- for (i=0;i<(LPC_INPUT-samples_per_channel)*channels;i++) enc->lpc_buffer[i] = enc->lpc_buffer[samples_per_channel*channels + i];
- for (i=0;i<samples_per_channel*channels;i++) enc->lpc_buffer[(LPC_INPUT-samples_per_channel)*channels + i] = pcm[i];
- } else {
- for (i=0;i<LPC_INPUT*channels;i++) enc->lpc_buffer[i] = pcm[(samples_per_channel-LPC_INPUT)*channels + i];
- }
- }
- do {
- int i;
- spx_uint32_t in_samples, out_samples;
- out_samples = BUFFER_SAMPLES-enc->buffer_end;
- if (enc->re != NULL) {
- in_samples = samples_per_channel;
- speex_resampler_process_interleaved_float(enc->re, pcm, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
- } else {
- int curr;
- curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
- for (i=0;i<channels*curr;i++) {
- enc->buffer[channels*enc->buffer_end+i] = pcm[i];
- }
- in_samples = out_samples = curr;
- }
- enc->buffer_end += out_samples;
- pcm += in_samples*channels;
- samples_per_channel -= in_samples;
- encode_buffer(enc);
- if (enc->unrecoverable) return enc->unrecoverable;
- } while (samples_per_channel > 0);
- return OPE_OK;
- }
- #define CONVERT_BUFFER 4096
- /* Add/encode any number of int16 samples to the file. */
- int ope_encoder_write(OggOpusEnc *enc, const opus_int16 *pcm, int samples_per_channel) {
- int channels = enc->channels;
- if (enc->unrecoverable) return enc->unrecoverable;
- enc->last_stream->header_is_frozen = 1;
- if (!enc->streams->stream_is_init) init_stream(enc);
- if (samples_per_channel < 0) return OPE_BAD_ARG;
- enc->write_granule += samples_per_channel;
- enc->last_stream->end_granule = enc->write_granule;
- if (enc->lpc_buffer) {
- int i;
- if (samples_per_channel < LPC_INPUT) {
- for (i=0;i<(LPC_INPUT-samples_per_channel)*channels;i++) enc->lpc_buffer[i] = enc->lpc_buffer[samples_per_channel*channels + i];
- for (i=0;i<samples_per_channel*channels;i++) enc->lpc_buffer[(LPC_INPUT-samples_per_channel)*channels + i] = (1.f/32768)*pcm[i];
- } else {
- for (i=0;i<LPC_INPUT*channels;i++) enc->lpc_buffer[i] = (1.f/32768)*pcm[(samples_per_channel-LPC_INPUT)*channels + i];
- }
- }
- do {
- int i;
- spx_uint32_t in_samples, out_samples;
- out_samples = BUFFER_SAMPLES-enc->buffer_end;
- if (enc->re != NULL) {
- float buf[CONVERT_BUFFER];
- in_samples = MIN(CONVERT_BUFFER/channels, samples_per_channel);
- for (i=0;i<channels*(int)in_samples;i++) {
- buf[i] = (1.f/32768)*pcm[i];
- }
- speex_resampler_process_interleaved_float(enc->re, buf, &in_samples, &enc->buffer[channels*enc->buffer_end], &out_samples);
- } else {
- int curr;
- curr = MIN((spx_uint32_t)samples_per_channel, out_samples);
- for (i=0;i<channels*curr;i++) {
- enc->buffer[channels*enc->buffer_end+i] = (1.f/32768)*pcm[i];
- }
- in_samples = out_samples = curr;
- }
- enc->buffer_end += out_samples;
- pcm += in_samples*channels;
- samples_per_channel -= in_samples;
- encode_buffer(enc);
- if (enc->unrecoverable) return enc->unrecoverable;
- } while (samples_per_channel > 0);
- return OPE_OK;
- }
- /* Get the next page from the stream. Returns 1 if there is a page available, 0 if not. */
- int ope_encoder_get_page(OggOpusEnc *enc, unsigned char **page, opus_int32 *len, int flush) {
- if (enc->unrecoverable) return enc->unrecoverable;
- if (!enc->pull_api) return 0;
- else {
- if (flush) oggp_flush_page(enc->oggp);
- return oggp_get_next_page(enc->oggp, page, len);
- }
- }
- static void extend_signal(float *x, int before, int after, int channels);
- int ope_encoder_drain(OggOpusEnc *enc) {
- int pad_samples;
- int resampler_drain = 0;
- if (enc->unrecoverable) return enc->unrecoverable;
- /* Check if it's already been drained. */
- if (enc->streams == NULL) return OPE_TOO_LATE;
- if (enc->re) resampler_drain = speex_resampler_get_output_latency(enc->re);
- pad_samples = MAX(LPC_PADDING, enc->global_granule_offset + enc->frame_size + resampler_drain + 1);
- if (!enc->streams->stream_is_init) init_stream(enc);
- shift_buffer(enc);
- assert(enc->buffer_end + pad_samples <= BUFFER_SAMPLES);
- memset(&enc->buffer[enc->channels*enc->buffer_end], 0, pad_samples*enc->channels*sizeof(enc->buffer[0]));
- if (enc->re) {
- spx_uint32_t in_samples, out_samples;
- extend_signal(&enc->lpc_buffer[LPC_INPUT*enc->channels], LPC_INPUT, LPC_PADDING, enc->channels);
- do {
- in_samples = LPC_PADDING;
- out_samples = pad_samples;
- speex_resampler_process_interleaved_float(enc->re, &enc->lpc_buffer[LPC_INPUT*enc->channels], &in_samples, &enc->buffer[enc->channels*enc->buffer_end], &out_samples);
- enc->buffer_end += out_samples;
- pad_samples -= out_samples;
- /* If we don't have enough padding, zero all zeros and repeat. */
- memset(&enc->lpc_buffer[LPC_INPUT*enc->channels], 0, LPC_PADDING*enc->channels*sizeof(enc->lpc_buffer[0]));
- } while (pad_samples > 0);
- } else {
- extend_signal(&enc->buffer[enc->channels*enc->buffer_end], enc->buffer_end, LPC_PADDING, enc->channels);
- enc->buffer_end += pad_samples;
- }
- enc->decision_delay = 0;
- enc->draining = 1;
- assert(enc->buffer_end <= BUFFER_SAMPLES);
- encode_buffer(enc);
- if (enc->unrecoverable) return enc->unrecoverable;
- /* Draining should have called all the streams to complete. */
- assert(enc->streams == NULL);
- return OPE_OK;
- }
- void ope_encoder_destroy(OggOpusEnc *enc) {
- EncStream *stream;
- stream = enc->streams;
- while (stream != NULL) {
- EncStream *tmp = stream;
- stream = stream->next;
- /* Ignore any error on close. */
- if (tmp->close_at_end && !enc->pull_api) enc->callbacks.close(tmp->user_data);
- stream_destroy(tmp);
- }
- if (enc->chaining_keyframe) free(enc->chaining_keyframe);
- free(enc->buffer);
- if (enc->oggp) oggp_destroy(enc->oggp);
- opeint_encoder_cleanup(&enc->st);
- if (enc->re) speex_resampler_destroy(enc->re);
- if (enc->lpc_buffer) free(enc->lpc_buffer);
- free(enc);
- }
- /* Ends the stream and create a new stream within the same file. */
- int ope_encoder_chain_current(OggOpusEnc *enc, OggOpusComments *comments) {
- enc->last_stream->close_at_end = 0;
- return ope_encoder_continue_new_callbacks(enc, enc->last_stream->user_data, comments);
- }
- /* Ends the stream and create a new file. */
- int ope_encoder_continue_new_file(OggOpusEnc *enc, const char *path, OggOpusComments *comments) {
- int ret;
- struct StdioObject *obj;
- if (!(obj = malloc(sizeof(*obj)))) return OPE_ALLOC_FAIL;
- obj->file = opeint_fopen(path, "wb");
- if (!obj->file) {
- free(obj);
- /* By trying to open the file first, we can recover if we can't open it. */
- return OPE_CANNOT_OPEN;
- }
- ret = ope_encoder_continue_new_callbacks(enc, obj, comments);
- if (ret == OPE_OK) return ret;
- fclose(obj->file);
- free(obj);
- return ret;
- }
- /* Ends the stream and create a new file (callback-based). */
- int ope_encoder_continue_new_callbacks(OggOpusEnc *enc, void *user_data, OggOpusComments *comments) {
- EncStream *new_stream;
- if (enc->unrecoverable) return enc->unrecoverable;
- assert(enc->streams);
- assert(enc->last_stream);
- new_stream = stream_create(comments);
- if (!new_stream) return OPE_ALLOC_FAIL;
- new_stream->user_data = user_data;
- new_stream->end_granule = enc->write_granule;
- enc->last_stream->next = new_stream;
- enc->last_stream = new_stream;
- return OPE_OK;
- }
- int ope_encoder_flush_header(OggOpusEnc *enc) {
- if (enc->unrecoverable) return enc->unrecoverable;
- if (enc->last_stream->header_is_frozen) return OPE_TOO_LATE;
- if (enc->last_stream->stream_is_init) return OPE_TOO_LATE;
- else init_stream(enc);
- return OPE_OK;
- }
- /* Goes straight to the libopus ctl() functions. */
- int ope_encoder_ctl(OggOpusEnc *enc, int request, ...) {
- int ret;
- int translate;
- va_list ap;
- if (enc->unrecoverable) return enc->unrecoverable;
- va_start(ap, request);
- ret = OPE_OK;
- switch (request) {
- case OPUS_SET_APPLICATION_REQUEST:
- case OPUS_SET_BITRATE_REQUEST:
- case OPUS_SET_MAX_BANDWIDTH_REQUEST:
- case OPUS_SET_VBR_REQUEST:
- case OPUS_SET_BANDWIDTH_REQUEST:
- case OPUS_SET_COMPLEXITY_REQUEST:
- case OPUS_SET_INBAND_FEC_REQUEST:
- case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
- case OPUS_SET_DTX_REQUEST:
- case OPUS_SET_VBR_CONSTRAINT_REQUEST:
- case OPUS_SET_FORCE_CHANNELS_REQUEST:
- case OPUS_SET_SIGNAL_REQUEST:
- case OPUS_SET_LSB_DEPTH_REQUEST:
- case OPUS_SET_PREDICTION_DISABLED_REQUEST:
- #ifdef OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST
- case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST:
- #endif
- {
- opus_int32 value = va_arg(ap, opus_int32);
- ret = opeint_encoder_ctl2(&enc->st, request, value);
- }
- break;
- case OPUS_GET_LOOKAHEAD_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- ret = opeint_encoder_ctl(&enc->st, OPUS_GET_LOOKAHEAD(value));
- }
- break;
- case OPUS_SET_EXPERT_FRAME_DURATION_REQUEST:
- {
- opus_int32 value = va_arg(ap, opus_int32);
- int max_supported = OPUS_FRAMESIZE_60_MS;
- #ifdef OPUS_FRAMESIZE_120_MS
- max_supported = OPUS_FRAMESIZE_120_MS;
- #endif
- if (value < OPUS_FRAMESIZE_2_5_MS || value > max_supported) {
- ret = OPUS_UNIMPLEMENTED;
- break;
- }
- ret = opeint_encoder_ctl(&enc->st, OPUS_SET_EXPERT_FRAME_DURATION(value));
- if (ret == OPUS_OK) {
- enc->frame_size = compute_frame_samples(value);
- enc->frame_size_request = value;
- }
- }
- break;
- case OPUS_GET_APPLICATION_REQUEST:
- case OPUS_GET_BITRATE_REQUEST:
- case OPUS_GET_MAX_BANDWIDTH_REQUEST:
- case OPUS_GET_VBR_REQUEST:
- case OPUS_GET_BANDWIDTH_REQUEST:
- case OPUS_GET_COMPLEXITY_REQUEST:
- case OPUS_GET_INBAND_FEC_REQUEST:
- case OPUS_GET_PACKET_LOSS_PERC_REQUEST:
- case OPUS_GET_DTX_REQUEST:
- case OPUS_GET_VBR_CONSTRAINT_REQUEST:
- case OPUS_GET_FORCE_CHANNELS_REQUEST:
- case OPUS_GET_SIGNAL_REQUEST:
- case OPUS_GET_LSB_DEPTH_REQUEST:
- case OPUS_GET_PREDICTION_DISABLED_REQUEST:
- #ifdef OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST
- case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST:
- #endif
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- ret = opeint_encoder_ctl2(&enc->st, request, value);
- }
- break;
- case OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST:
- {
- opus_int32 stream_id;
- OpusEncoder **value;
- stream_id = va_arg(ap, opus_int32);
- value = va_arg(ap, OpusEncoder**);
- opeint_encoder_ctl(&enc->st, OPUS_MULTISTREAM_GET_ENCODER_STATE(stream_id, value));
- }
- break;
- /* ****************** libopusenc-specific requests. ********************** */
- case OPE_SET_DECISION_DELAY_REQUEST:
- {
- opus_int32 value = va_arg(ap, opus_int32);
- if (value < 0) {
- ret = OPE_BAD_ARG;
- break;
- }
- value = MIN(value, MAX_LOOKAHEAD);
- enc->decision_delay = value;
- }
- break;
- case OPE_GET_DECISION_DELAY_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- *value = enc->decision_delay;
- }
- break;
- case OPE_SET_MUXING_DELAY_REQUEST:
- {
- opus_int32 value = va_arg(ap, opus_int32);
- if (value < 0) {
- ret = OPE_BAD_ARG;
- break;
- }
- enc->max_ogg_delay = value;
- if (enc->oggp) oggp_set_muxing_delay(enc->oggp, enc->max_ogg_delay);
- }
- break;
- case OPE_GET_MUXING_DELAY_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- *value = enc->max_ogg_delay;
- }
- break;
- case OPE_SET_COMMENT_PADDING_REQUEST:
- {
- opus_int32 value = va_arg(ap, opus_int32);
- if (value < 0) {
- ret = OPE_BAD_ARG;
- break;
- }
- enc->comment_padding = value;
- ret = OPE_OK;
- }
- break;
- case OPE_GET_COMMENT_PADDING_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- *value = enc->comment_padding;
- }
- break;
- case OPE_SET_SERIALNO_REQUEST:
- {
- opus_int32 value = va_arg(ap, opus_int32);
- if (!enc->last_stream || enc->last_stream->header_is_frozen) {
- ret = OPE_TOO_LATE;
- break;
- }
- enc->last_stream->serialno = value;
- enc->last_stream->serialno_is_set = 1;
- ret = OPE_OK;
- }
- break;
- case OPE_GET_SERIALNO_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- *value = enc->last_stream->serialno;
- }
- break;
- case OPE_SET_PACKET_CALLBACK_REQUEST:
- {
- ope_packet_func value = va_arg(ap, ope_packet_func);
- void *data = va_arg(ap, void *);
- enc->packet_callback = value;
- enc->packet_callback_data = data;
- ret = OPE_OK;
- }
- break;
- case OPE_SET_HEADER_GAIN_REQUEST:
- {
- opus_int32 value = va_arg(ap, opus_int32);
- if (!enc->last_stream || enc->last_stream->header_is_frozen) {
- ret = OPE_TOO_LATE;
- break;
- }
- enc->header.gain = value;
- ret = OPE_OK;
- }
- break;
- case OPE_GET_HEADER_GAIN_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- *value = enc->header.gain;
- }
- break;
- case OPE_GET_NB_STREAMS_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- *value = enc->header.nb_streams;
- }
- break;
- case OPE_GET_NB_COUPLED_STREAMS_REQUEST:
- {
- opus_int32 *value = va_arg(ap, opus_int32*);
- *value = enc->header.nb_coupled;
- }
- break;
- default:
- ret = OPUS_UNIMPLEMENTED;
- }
- va_end(ap);
- translate = ret != 0 && (request < 14000 || (ret < 0 && ret >= -10));
- if (translate) {
- if (ret == OPUS_BAD_ARG) ret = OPE_BAD_ARG;
- else if (ret == OPUS_INTERNAL_ERROR) ret = OPE_INTERNAL_ERROR;
- else if (ret == OPUS_UNIMPLEMENTED) ret = OPE_UNIMPLEMENTED;
- else if (ret == OPUS_ALLOC_FAIL) ret = OPE_ALLOC_FAIL;
- else ret = OPE_INTERNAL_ERROR;
- }
- assert(ret == 0 || ret < -10);
- return ret;
- }
- const char *ope_strerror(int error) {
- static const char * const ope_error_strings[] = {
- "cannot open file",
- "call cannot be made at this point",
- "invalid picture file",
- "invalid icon file (pictures of type 1 MUST be 32x32 PNGs)",
- "write failed",
- "close failed"
- };
- if (error == 0) return "success";
- else if (error >= -10) return "unknown error";
- else if (error > -30) return opus_strerror(error+10);
- else if (error >= OPE_CLOSE_FAIL) return ope_error_strings[-error-30];
- else return "unknown error";
- }
- const char *ope_get_version_string(void)
- {
- return "libopusenc " PACKAGE_VERSION;
- }
- int ope_get_abi_version(void) {
- return OPE_ABI_VERSION;
- }
- static void vorbis_lpc_from_data(float *data, float *lpci, int n, int stride);
- static void extend_signal(float *x, int before, int after, int channels) {
- int c;
- int i;
- float window[LPC_PADDING];
- if (after==0) return;
- before = MIN(before, LPC_INPUT);
- if (before < 4*LPC_ORDER) {
- int i;
- for (i=0;i<after*channels;i++) x[i] = 0;
- return;
- }
- {
- /* Generate Window using a resonating IIR aka Goertzel's algorithm. */
- float m0=1, m1=.5*LPC_GOERTZEL_CONST;
- float a1 = LPC_GOERTZEL_CONST;
- window[0] = 1;
- for (i=1;i<LPC_PADDING;i++) {
- window[i] = a1*m0 - m1;
- m1 = m0;
- m0 = window[i];
- }
- for (i=0;i<LPC_PADDING;i++) window[i] = .5+.5*window[i];
- }
- for (c=0;c<channels;c++) {
- float lpc[LPC_ORDER];
- vorbis_lpc_from_data(x-channels*before+c, lpc, before, channels);
- for (i=0;i<after;i++) {
- float sum;
- int j;
- sum = 0;
- for (j=0;j<LPC_ORDER;j++) sum -= x[(i-j-1)*channels + c]*lpc[j];
- x[i*channels + c] = sum;
- }
- for (i=0;i<after;i++) x[i*channels + c] *= window[i];
- }
- }
- /* Some of these routines (autocorrelator, LPC coefficient estimator)
- are derived from code written by Jutta Degener and Carsten Bormann;
- thus we include their copyright below. The entirety of this file
- is freely redistributable on the condition that both of these
- copyright notices are preserved without modification. */
- /* Preserved Copyright: *********************************************/
- /* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann,
- Technische Universita"t Berlin
- Any use of this software is permitted provided that this notice is not
- removed and that neither the authors nor the Technische Universita"t
- Berlin are deemed to have made any representations as to the
- suitability of this software for any purpose nor are held responsible
- for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR
- THIS SOFTWARE.
- As a matter of courtesy, the authors request to be informed about uses
- this software has found, about bugs in this software, and about any
- improvements that may be of general interest.
- Berlin, 28.11.1994
- Jutta Degener
- Carsten Bormann
- *********************************************************************/
- static void vorbis_lpc_from_data(float *data, float *lpci, int n, int stride) {
- double aut[LPC_ORDER+1];
- double lpc[LPC_ORDER];
- double error;
- double epsilon;
- int i,j;
- /* FIXME: Apply a window to the input. */
- /* autocorrelation, p+1 lag coefficients */
- j=LPC_ORDER+1;
- while(j--){
- double d=0; /* double needed for accumulator depth */
- for(i=j;i<n;i++)d+=(double)data[i*stride]*data[(i-j)*stride];
- aut[j]=d;
- }
- /* Apply lag windowing (better than bandwidth expansion) */
- if (LPC_ORDER <= 64) {
- for (i=1;i<=LPC_ORDER;i++) {
- /* Approximate this gaussian for low enough order. */
- /* aut[i] *= exp(-.5*(2*M_PI*.002*i)*(2*M_PI*.002*i));*/
- aut[i] -= aut[i]*(0.008f*0.008f)*i*i;
- }
- }
- /* Generate lpc coefficients from autocorr values */
- /* set our noise floor to about -100dB */
- error=aut[0] * (1. + 1e-7);
- epsilon=1e-6*aut[0]+1e-7;
- for(i=0;i<LPC_ORDER;i++){
- double r= -aut[i+1];
- if(error<epsilon){
- memset(lpc+i,0,(LPC_ORDER-i)*sizeof(*lpc));
- goto done;
- }
- /* Sum up this iteration's reflection coefficient; note that in
- Vorbis we don't save it. If anyone wants to recycle this code
- and needs reflection coefficients, save the results of 'r' from
- each iteration. */
- for(j=0;j<i;j++)r-=lpc[j]*aut[i-j];
- r/=error;
- /* Update LPC coefficients and total error */
- lpc[i]=r;
- for(j=0;j<i/2;j++){
- double tmp=lpc[j];
- lpc[j]+=r*lpc[i-1-j];
- lpc[i-1-j]+=r*tmp;
- }
- if(i&1)lpc[j]+=lpc[j]*r;
- error*=1.-r*r;
- }
- done:
- /* slightly damp the filter */
- {
- double g = .999;
- double damp = g;
- for(j=0;j<LPC_ORDER;j++){
- lpc[j]*=damp;
- damp*=g;
- }
- }
- for(j=0;j<LPC_ORDER;j++)lpci[j]=(float)lpc[j];
- }
|