Mercurial > otakunoraifu
diff music2/koedec_ogg.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | 422f3cb3614b |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/music2/koedec_ogg.cc @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2004-2006 Kazunori "jagarl" Ueno + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include<stdio.h> +#include<string.h> +#include<stdlib.h> +#include"music.h" +#include"wavfile.h" + +extern int is_koe_ogg(char* head); +extern char* decode_koe_ogg(AvgKoeInfo info, int* dest_len); + +extern int is_koe_ogg(char* head) { +#if HAVE_LIBVORBISFILE || HAVE_LIBVORBISIDEC + if (strncmp(head, "OggS", 4) == 0) return 1; + else +#endif + return 0; +} + +#if HAVE_LIBVORBISFILE || HAVE_LIBVORBISIDEC + +#if HAVE_LIBVORBISFILE +#include<vorbis/vorbisfile.h> +#else /* HAVE_LIBVORBISIDEC */ +#include<tremor/ivorbiscodec.h> +#include<tremor/ivorbisfile.h> +#endif + +#define INITSIZE 65536 + +static int cur_size = 0; +static char* out = 0; +static void Resize(void) { + char* new_out = (char*)realloc(out, cur_size+INITSIZE); + if (new_out == 0) { + new_out = (char*)malloc(cur_size+INITSIZE); + memcpy(new_out, out, cur_size); + free(out); + } + out = new_out; + cur_size += INITSIZE; +} + +struct OggInfo { + FILE* stream; + int length; + int offset; +}; +/* ogg stream 読み込み用の dummy callback */ +static size_t ogg_readfunc(void* ptr, size_t size, size_t nmemb, void* datasource) { + OggInfo* info = (OggInfo*)datasource; + int pt = ftell(info->stream) - info->offset; + if (pt+size*nmemb > info->length) { + nmemb = (info->length-pt) / size; + } + return fread(ptr, size, nmemb, info->stream); +} +static int ogg_seekfunc(void* datasource, ogg_int64_t new_offset, int whence) { + int pt; + OggInfo* info = (OggInfo*)datasource; + if (whence == SEEK_SET) pt = info->offset + new_offset; + else if (whence == SEEK_CUR) pt = ftell(info->stream) + new_offset; + else if (whence == SEEK_END) pt = info->offset + info->length + new_offset; + int r = fseek(info->stream, pt, 0); + return r; +} +static long ogg_tellfunc(void* datasource) { + OggInfo* info = (OggInfo*)datasource; + int pos = ftell(info->stream); + if (pos == -1) return -1; + return pos-info->offset; +} +static int ogg_closefunc(void* datasource) { + return 0; +} + +extern char* decode_koe_ogg(AvgKoeInfo info, int* dest_len) { + if (info.stream == 0) return 0; + // Voice ファイルを直接指定すると全ストリームを再生してしまうので + // 必要な部分だけ切り出して callback 経由で帰す + fseek(info.stream, info.offset, 0); + + ov_callbacks callback; + callback.read_func = &ogg_readfunc; + callback.seek_func = &ogg_seekfunc; + callback.close_func = &ogg_closefunc; + callback.tell_func = &ogg_tellfunc; + + OggInfo oinfo; + oinfo.stream = info.stream; + oinfo.length = info.length; + oinfo.offset = info.offset; + + OggVorbis_File vf; + int r = ov_open_callbacks((void*)&oinfo, &vf, 0, 0, callback); + if (r != 0) { + fprintf(stderr,"ogg stream err: %d\n",r); + return 0; + } + vorbis_info* vinfo = ov_info(&vf, 0); + info.rate = vinfo->rate; + int channels = vinfo->channels; + + int cur = 0x2c; + cur_size = INITSIZE; + out = (char*)malloc(cur_size); + + int current_section; + do { +#if HAVE_LIBVORBISFILE + r = ov_read(&vf, out+cur, cur_size-cur, 0, 2, 1, 0); +#else /* HAVE_LIBVORBISIDEC */ + r = ov_read(&vf, out+cur, cur_size-cur, ¤t_section); +#endif + if (r <= 0) break; + cur += r; + if (cur_size-INITSIZE/4 < cur) Resize(); + } while(1); + ov_clear(&vf); + + *dest_len = cur; // うまくコンバートできてるのかなあ…… + const char* header = MakeWavHeader(info.rate, channels, 2, cur); + memcpy(out, header, 0x2c); + + char* ret = out; + out = 0; + + return ret; +} +struct OggFILE_impl { + OggVorbis_File vf; + ov_callbacks callback; + OggInfo oinfo; + OggFILE_impl(FILE*, int); +}; + +OggFILE_impl::OggFILE_impl(FILE* stream, int length) { + callback.read_func = &ogg_readfunc; + callback.seek_func = &ogg_seekfunc; + callback.close_func = &ogg_closefunc; + callback.tell_func = &ogg_tellfunc; + oinfo.stream = stream; + oinfo.length = length; + oinfo.offset = ftell(stream); +} + +OggFILE::OggFILE(FILE* stream, int len) { + pimpl = new OggFILE_impl(stream, len); + int r = ov_open_callbacks( (void*)&(pimpl->oinfo), &(pimpl->vf), 0, 0, pimpl->callback); + if (r != 0) { + delete pimpl; + pimpl = 0; + return; + } + vorbis_info* vinfo = ov_info(&(pimpl->vf), 0); + wavinfo.SamplingRate = vinfo->rate; + wavinfo.Channels = vinfo->channels; + wavinfo.DataBits = 16; +} +OggFILE::~OggFILE() { + if (pimpl) { + ov_clear(&(pimpl->vf)); + fclose(pimpl->oinfo.stream); + delete pimpl; + } +} +int OggFILE::Read(char* buf, int blksize, int blklen) { + if (pimpl == 0) return -1; + int current_section; +#if HAVE_LIBVORBISFILE + int r = ov_read( &(pimpl->vf), buf, blksize*blklen, 0, 2, 1, 0); +#else /* HAVE_LIBVORBISIDEC */ + int r = ov_read( &(pimpl->vf), buf, blksize*blklen, ¤t_section); +#endif + if (r <= 0) { // end of file + return -1; + } + while(r < blksize*blklen) { +#if HAVE_LIBVORBISFILE + int dr = ov_read(&(pimpl->vf), buf+r, blksize*blklen-r, 0, 2, 1, 0); +#else /* HAVE_LIBVORBISIDEC */ + int dr = ov_read(&(pimpl->vf), buf+r, blksize*blklen-r, ¤t_section); +#endif + if (dr <= 0) break; + r += dr; + } + return r / blksize; +} +void OggFILE::Seek(int count) { + ov_pcm_seek(&(pimpl->vf), count); + return; +} +#else +extern char* decode_koe_ogg(AvgKoeInfo info, int* dest_len) { + return 0; +} +OggFILE::OggFILE(FILE* stream, int a) {pimpl = 0;} +OggFILE::~OggFILE(){} +void OggFILE::Seek(int count){} +int OggFILE::Read(char* buf, int blksize, int blklen){return -1;} +#endif