From 6c8fe2c0bd079446a5f33e210186a2840095df96 Mon Sep 17 00:00:00 2001 From: Stefan Sayer Date: Tue, 27 Feb 2007 02:55:39 +0000 Subject: [PATCH] mem cached audio files implementation - improves performance 4-5% over announcement from file (G711) see apps/examples/cacheannounce for an example how to use git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@247 8eb893ce-cfd4-0310-b710-fb5ebe64c474 --- apps/examples/cacheannounce/CacheAnnounce.cpp | 134 +++++++++++ apps/examples/cacheannounce/CacheAnnounce.h | 80 +++++++ apps/examples/cacheannounce/Makefile | 9 + .../cacheannounce/Readme.cacheannounce | 13 + .../cacheannounce/etc/cacheannounce.conf | 7 + core/AmCachedAudioFile.cpp | 222 ++++++++++++++++++ core/AmCachedAudioFile.h | 109 +++++++++ core/amci/amci.h | 50 +++- core/plug-in/ilbc/ilbc.c | 2 +- core/plug-in/wav/wav.c | 4 +- core/plug-in/wav/wav_hdr.c | 103 ++++++++ core/plug-in/wav/wav_hdr.h | 6 + 12 files changed, 733 insertions(+), 6 deletions(-) create mode 100644 apps/examples/cacheannounce/CacheAnnounce.cpp create mode 100644 apps/examples/cacheannounce/CacheAnnounce.h create mode 100644 apps/examples/cacheannounce/Makefile create mode 100644 apps/examples/cacheannounce/Readme.cacheannounce create mode 100644 apps/examples/cacheannounce/etc/cacheannounce.conf create mode 100644 core/AmCachedAudioFile.cpp create mode 100644 core/AmCachedAudioFile.h diff --git a/apps/examples/cacheannounce/CacheAnnounce.cpp b/apps/examples/cacheannounce/CacheAnnounce.cpp new file mode 100644 index 00000000..5675e228 --- /dev/null +++ b/apps/examples/cacheannounce/CacheAnnounce.cpp @@ -0,0 +1,134 @@ +/* + * $Id: CacheAnnounce.cpp 206 2007-02-05 14:45:07Z sayer $ + * + * Copyright (C) 2002-2003 Fhg Fokus + * Copyright (C) 2007 iptego GmbH + * + * This file is part of sems, a free SIP media server. + * + * sems is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * sems is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "CacheAnnounce.h" +#include "AmConfig.h" +#include "AmUtils.h" + +#include "sems.h" +#include "log.h" + +#define MOD_NAME "cacheannounce" + +EXPORT_SESSION_FACTORY(CacheAnnounceFactory,MOD_NAME); + +string CacheAnnounceFactory::AnnouncePath; +string CacheAnnounceFactory::AnnounceFile; + +CacheAnnounceFactory::CacheAnnounceFactory(const string& _app_name) + : AmSessionFactory(_app_name) +{ +} + +int CacheAnnounceFactory::onLoad() +{ + AmConfigReader cfg; + if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) + return -1; + + // get application specific global parameters + configureModule(cfg); + + AnnouncePath = cfg.getParameter("announce_path",ANNOUNCE_PATH); + if( !AnnouncePath.empty() + && AnnouncePath[AnnouncePath.length()-1] != '/' ) + AnnouncePath += "/"; + + AnnounceFile = cfg.getParameter("default_announce",ANNOUNCE_FILE); + + string announce_file = AnnouncePath + AnnounceFile; + if(!file_exists(announce_file)){ + ERROR("default file for cacheannounce module does not exist ('%s').\n", + announce_file.c_str()); + return -1; + } + + if (ann_cache.load(announce_file)) { + ERROR("file '%s' could not be cached.\n", + announce_file.c_str()); + return -1; + } + + return 0; +} + +AmSession* CacheAnnounceFactory::onInvite(const AmSipRequest& req) +{ + return new CacheAnnounceDialog(&ann_cache); +} + +CacheAnnounceDialog::CacheAnnounceDialog(AmFileCache* announce) + : announce(announce) +{ +} + +CacheAnnounceDialog::~CacheAnnounceDialog() +{ +} + +void CacheAnnounceDialog::onSessionStart(const AmSipRequest& req) +{ + DBG("CacheAnnounceDialog::onSessionStart\n"); + startSession(); +} + +void CacheAnnounceDialog::onSessionStart(const AmSipReply& rep) +{ + DBG("CacheAnnounceDialog::onSessionStart (SEMS originator mode)\n"); + startSession(); +} + +void CacheAnnounceDialog::startSession(){ + setDtmfDetectionEnabled(false); + + wav_file.reset(new AmCachedAudioFile(announce)); + if (!wav_file->is_good()) + throw AmSession::Exception(500, "Internal Err"); + + setOutput(wav_file.get()); +} + +void CacheAnnounceDialog::onBye(const AmSipRequest& req) +{ + DBG("onBye: stopSession\n"); + setStopped(); +} + + +void CacheAnnounceDialog::process(AmEvent* event) +{ + + AmAudioEvent* audio_event = dynamic_cast(event); + if(audio_event && (audio_event->event_id == AmAudioEvent::cleared)){ + dlg.bye(); + setStopped(); + return; + } + + AmSession::process(event); +} diff --git a/apps/examples/cacheannounce/CacheAnnounce.h b/apps/examples/cacheannounce/CacheAnnounce.h new file mode 100644 index 00000000..3380eac0 --- /dev/null +++ b/apps/examples/cacheannounce/CacheAnnounce.h @@ -0,0 +1,80 @@ +/* + * $Id: CacheAnnounce.h 145 2006-11-26 00:01:18Z sayer $ + * + * Copyright (C) 2002-2003 Fhg Fokus + * Copyright (C) 2007 iptego GmbH + * + * This file is part of sems, a free SIP media server. + * + * sems is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * sems is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CACHEANNOUNCE_H_ +#define _CACHEANNOUNCE_H_ + +#include "AmSession.h" +#include "AmConfigReader.h" + +#include "AmCachedAudioFile.h" + +#include +using std::auto_ptr; + +#include +using std::string; + +class CacheAnnounceFactory: public AmSessionFactory +{ + + AmFileCache ann_cache; + +public: + static string AnnouncePath; + static string AnnounceFile; + + CacheAnnounceFactory(const string& _app_name); + + int onLoad(); + AmSession* onInvite(const AmSipRequest& req); +}; + +class CacheAnnounceDialog : public AmSession +{ + auto_ptr wav_file; + AmFileCache* announce; + + public: + CacheAnnounceDialog(AmFileCache* announce); + ~CacheAnnounceDialog(); + + void onSessionStart(const AmSipRequest& req); + void onSessionStart(const AmSipReply& rep); + void startSession(); + void onBye(const AmSipRequest& req); + void onDtmf(int event, int duration_msec) {} + + void process(AmEvent* event); +}; + +#endif +// Local Variables: +// mode:C++ +// End: + diff --git a/apps/examples/cacheannounce/Makefile b/apps/examples/cacheannounce/Makefile new file mode 100644 index 00000000..9234f16f --- /dev/null +++ b/apps/examples/cacheannounce/Makefile @@ -0,0 +1,9 @@ +plug_in_name = cacheannounce + +module_ldflags = +module_cflags = + +extra_install = $(plug_in_name)_audio + +COREPATH ?=../../../core +include $(COREPATH)/plug-in/Makefile.app_module diff --git a/apps/examples/cacheannounce/Readme.cacheannounce b/apps/examples/cacheannounce/Readme.cacheannounce new file mode 100644 index 00000000..56a3ad55 --- /dev/null +++ b/apps/examples/cacheannounce/Readme.cacheannounce @@ -0,0 +1,13 @@ +cacheannounce always plays the default announce to the caller. + +This is a example of how to use the mem cached versions of AmAudioFile: +AmFileCache/AmCachedAudioFile. + +The AmFileCache class can load a file once (AmFileCache::load), +and the AmCachedAudioFile then uses the file from memory. AmFileCache +objects can e.g. be created at startup and passed to the session object +which creates AmCachedAudioFile objects to play back the files. + +Measurements with 100 calls playing back default_en.wav encoded as PCMU +have shown a 4-5% performance increase (192,607,161 Ir's vs. 201,569,407 Ir's) +over the normal not cached announcement application. \ No newline at end of file diff --git a/apps/examples/cacheannounce/etc/cacheannounce.conf b/apps/examples/cacheannounce/etc/cacheannounce.conf new file mode 100644 index 00000000..584982b2 --- /dev/null +++ b/apps/examples/cacheannounce/etc/cacheannounce.conf @@ -0,0 +1,7 @@ +#CFGOPTION_SEMS_ANNOUNCEPATH +announce_path=/usr/local/lib/sems/audio/ +#ENDCFGOPTION + +#CFGOPTION_SEMS_ANNOUNCEMENT +default_announce=default_en.wav +#ENDCFGOPTION diff --git a/core/AmCachedAudioFile.cpp b/core/AmCachedAudioFile.cpp new file mode 100644 index 00000000..39c15199 --- /dev/null +++ b/core/AmCachedAudioFile.cpp @@ -0,0 +1,222 @@ +/* + * $Id: AmFileCache.cpp 145 2006-11-26 00:01:18Z sayer $ + * + * Copyright (C) 2007 iptego GmbH + * + * This file is part of sems, a free SIP media server. + * + * sems is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * sems is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "AmCachedAudioFile.h" +#include "AmUtils.h" +#include "log.h" +#include "AmPlugIn.h" + +#include +#include +#include +#include +#include + + + +using std::string; + +AmFileCache::AmFileCache() + : data(NULL), + data_size(0) +{ } + +AmFileCache::~AmFileCache() { + if ((data != NULL) && + munmap(data, data_size)) { + ERROR("while unmapping file.\n"); + } +} + +int AmFileCache::load(const string& filename) { + int fd; + struct stat sbuf; + + name = filename; + + if ((fd = open(name.c_str(), O_RDONLY)) == -1) { + ERROR("while opening file '%s' for caching.\n", + filename.c_str()); + return -1; + } + + if (fstat(fd, &sbuf) == -1) { + ERROR("cannot stat file '%s'.\n", + name.c_str()); + return -2; + } + + if ((data = mmap((caddr_t)0, sbuf.st_size, PROT_READ, MAP_PRIVATE, + fd, 0)) == (caddr_t)(-1)) { + ERROR("cannot mmap file '%s'.\n", + name.c_str()); + return -3; + } + + data_size = sbuf.st_size; + + return 0; +} + +int AmFileCache::read(void* buf, + size_t* pos, + size_t size) { + + if (*pos >= data_size) + return -1; // eof + + size_t r_size = size; + if (*pos+size > data_size) + r_size = data_size-*pos; + + if (r_size>0) { + memcpy(buf, (unsigned char*)data + *pos, r_size); + *pos+=r_size; + } + return r_size; +} + +inline size_t AmFileCache::getSize() { + return data_size; +} + +inline const string& AmFileCache::getFilename() { + return name; +} + + +AmCachedAudioFile::AmCachedAudioFile(AmFileCache* cache) + : cache(cache), loop(false), fpos(0), begin(0), good(false) +{ + if (!cache) { + ERROR("Need open file cache.\n"); + return; + } + + AmAudioFileFormat* f_fmt = fileName2Fmt(cache->getFilename()); + if(!f_fmt){ + ERROR("while trying to determine the format of '%s'\n", + cache->getFilename().c_str()); + return; + } + fmt.reset(f_fmt); + + amci_file_desc_t fd; + int ret = -1; + + fd.subtype = f_fmt->getSubtypeId(); + fd.channels = f_fmt->channels; + fd.rate = f_fmt->rate; + + long unsigned int ofpos = fpos; + + if( iofmt->mem_open && + !(ret = (*iofmt->mem_open)((unsigned char*)cache->getData(),cache->getSize(),&ofpos, + &fd,AmAudioFile::Read,f_fmt->getHCodecNoInit())) ) { + f_fmt->setSubtypeId(fd.subtype); + f_fmt->channels = fd.channels; + f_fmt->rate = fd.rate; + + begin = fpos = ofpos; + } + else { + if(!iofmt->mem_open) + ERROR("no mem_open function\n"); + else + ERROR("mem_open returned %d\n",ret); + close(); + return; + } + + good = true; + + return; +} + +AmCachedAudioFile::~AmCachedAudioFile() { +} + +AmAudioFileFormat* AmCachedAudioFile::fileName2Fmt(const string& name) +{ + string ext = file_extension(name); + if(ext == ""){ + ERROR("fileName2Fmt: file name has no extension (%s)",name.c_str()); + return NULL; + } + + iofmt = AmPlugIn::instance()->fileFormat("",ext); + if(!iofmt){ + ERROR("fileName2Fmt: could not find a format with that extension: '%s'",ext.c_str()); + return NULL; + } + + return new AmAudioFileFormat(iofmt->name); +} + +void AmCachedAudioFile::rewind() { + fpos = begin; +} + +/** Closes the file. */ +void AmCachedAudioFile::close() { + fpos = 0; +} + +/** Executes the handler's on_close. */ +void on_close() { +} + +int AmCachedAudioFile::read(unsigned int user_ts, unsigned int size) { + + if(!good){ + ERROR("AmAudioFile::read: file is not opened\n"); + return -1; + } + + int ret = cache->read((void*)((unsigned char*)samples),&fpos,size); + + //DBG("s = %i; ret = %i\n",s,ret); + if(loop.get() && (ret <= 0) && fpos==cache->getSize()){ + DBG("rewinding audio file...\n"); + rewind(); + ret = cache->read((void*)((unsigned char*)samples),&fpos, size); + } + + if(ret > 0 && (unsigned int)ret < size){ + DBG("0-stuffing packet: adding %i bytes (packet size=%i)\n",size-ret,size); + memset((unsigned char*)samples + ret,0,size-ret); + return size; + } + + return (fpos==cache->getSize() && !loop.get() ? -2 : ret); +} + +int AmCachedAudioFile::write(unsigned int user_ts, unsigned int size) { + ERROR("AmCachedAudioFile writing not supported!\n"); + return -1; +} + diff --git a/core/AmCachedAudioFile.h b/core/AmCachedAudioFile.h new file mode 100644 index 00000000..84f51907 --- /dev/null +++ b/core/AmCachedAudioFile.h @@ -0,0 +1,109 @@ +/* + * $Id: AmFileCache.h 145 2006-11-26 00:01:18Z sayer $ + * + * Copyright (C) 2007 iptego GmbH + * + * This file is part of sems, a free SIP media server. + * + * sems is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * sems is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _AMFILECACHE_H +#define _AMFILECACHE_H + +#include "AmAudio.h" + +#include + +/** + * The AmFileCache class loads a file once into memory + * to be used e.g. by AmCachedAudioFile. + */ +class AmFileCache +{ + void* data; + size_t data_size; + std::string name; + + public: + AmFileCache(); + ~AmFileCache(); + + /** load filename into memory + * @return 0 if everything's OK + */ + int load(const std::string& filename); + /** get the size of the file */ + size_t getSize(); + /** read size bytes from pos into buf */ + int read(void* buf, size_t* pos, size_t size); + /** get the filename */ + const string& getFilename(); + /** get a pointer to the file's data - use with caution! */ + void* getData() { return data; } +}; + +/** + * \brief AmAudio implementation for cached file + * + * This uses an AmFileCache instance to read the data + * rather than a file. + */ +class AmCachedAudioFile +: public AmAudio +{ + AmFileCache* cache; + /** current position */ + size_t fpos; + /** beginning of data in file */ + size_t begin; + bool good; + + /** @see AmAudio::read */ + int read(unsigned int user_ts, unsigned int size); + + /** @see AmAudio::write */ + int write(unsigned int user_ts, unsigned int size); + + /** get the file format from the file name */ + AmAudioFileFormat* fileName2Fmt(const string& name); + + /** Format of that file. @see fp, open(). */ + amci_inoutfmt_t* iofmt; + + public: + AmCachedAudioFile(AmFileCache* cache); + ~AmCachedAudioFile(); + + /** loop the file? */ + AmSharedVar loop; + + /** + * Rewind the file. + */ + void rewind(); + + /** Closes the file. */ + void close(); + + /** everything ok? */ + bool is_good() { return good; } +}; +#endif //_AMFILECACHE_H diff --git a/core/amci/amci.h b/core/amci/amci.h index 80e8f854..7160c3de 100644 --- a/core/amci/amci.h +++ b/core/amci/amci.h @@ -171,6 +171,44 @@ typedef int (*amci_file_close_t)( FILE* fptr, struct amci_codec_t *codec ); +/** + * File format handler's open function from memory area + * @param fptr [in] pointer to memory where file is loaded + * @param size [in] length of file in mem + * @param pos [out] position after open + * @param fmt_desc [out] file description + * @param options [in] options (see amci_inoutfmt_t) + * @param h_codec [in] handle of the codec + * @return if failure -1, else 0. + * @see amci_inoutfmt_t::open + */ +typedef int (*amci_file_mem_open_t)(unsigned char* mptr, + unsigned long size, + unsigned long* pos, + struct amci_file_desc_t* fmt_desc, + int options, + long h_codec + ); + +/** + * File format handler's mem close function (usually no-op) + * @param fptr [in] pointer to memory where file is loaded + * @param pos [in,out] position in memory + * @param fmt_desc [out] file description + * @param options [in] options (see amci_inoutfmt_t) + * @param h_codec [in] handle of the codec + * @param codec [in] codec structure + * @return if failure -1, else 0. + * @see amci_inoutfmt_t::on_close + */ +typedef int (*amci_file_mem_close_t)( unsigned char* mptr, + unsigned long* pos, + struct amci_file_desc_t* fmt_desc, + int options, + long h_codec, + struct amci_codec_t *codec + ); + /** * Codec's init function pointer. * @param format_parameters [in] parameters as passed by fmtp tag, 0 if none @@ -285,6 +323,12 @@ struct amci_inoutfmt_t { /** no options at the moment. */ amci_file_close_t on_close; + /** options: AMCI_RDONLY, AMCI_WRONLY. */ + amci_file_mem_open_t mem_open; + + /** no options at the moment. */ + amci_file_mem_close_t mem_close; + /** NULL terminated subtype array. */ struct amci_subtype_t* subtypes; @@ -428,7 +472,7 @@ struct amci_exports_t { * @hideinitializer */ #define END_FILE_FORMATS \ - { 0,0,0,0,0, \ + { 0,0,0,0,0,0,0, \ (struct amci_subtype_t[]) { {-1, 0, -1, -1, -1} } \ } \ } @@ -438,8 +482,8 @@ struct amci_exports_t { * see example media plug-in 'wav' (plug-in/wav/wav.c). * @hideinitializer */ -#define BEGIN_FILE_FORMAT(name,ext,email_content_type,open,on_close) \ - { name,ext,email_content_type,open,on_close, +#define BEGIN_FILE_FORMAT(name,ext,email_content_type,open,on_close,mem_open,mem_close) \ + { name,ext,email_content_type,open,on_close,mem_open,mem_close, /** * Portable export definition macro diff --git a/core/plug-in/ilbc/ilbc.c b/core/plug-in/ilbc/ilbc.c index edf60891..b24393d2 100644 --- a/core/plug-in/ilbc/ilbc.c +++ b/core/plug-in/ilbc/ilbc.c @@ -91,7 +91,7 @@ BEGIN_EXPORTS( "ilbc" ) END_PAYLOADS BEGIN_FILE_FORMATS - BEGIN_FILE_FORMAT( "iLBC", "ilbc", "audio/iLBC", iLBC_open, iLBC_close) + BEGIN_FILE_FORMAT( "iLBC", "ilbc", "audio/iLBC", iLBC_open, iLBC_close, 0, 0) BEGIN_SUBTYPES SUBTYPE( ILBC30, "iLBC30", 8000, 1, CODEC_ILBC ) SUBTYPE( ILBC20, "iLBC20", 8000, 1, CODEC_ILBC ) diff --git a/core/plug-in/wav/wav.c b/core/plug-in/wav/wav.c index 8044256d..3bb8db17 100644 --- a/core/plug-in/wav/wav.c +++ b/core/plug-in/wav/wav.c @@ -57,7 +57,7 @@ END_PAYLOADS BEGIN_FILE_FORMATS - BEGIN_FILE_FORMAT( "Wav", "wav", "application/x-wav", wav_open, wav_close) + BEGIN_FILE_FORMAT( "Wav", "wav", "application/x-wav", wav_open, wav_close, 0, 0) BEGIN_SUBTYPES SUBTYPE( WAV_PCM, "Pcm16", 2, -1, -1, CODEC_PCM16 ) SUBTYPE( WAV_ALAW, "A-Law", 1, -1, -1, CODEC_ALAW ) @@ -105,7 +105,7 @@ BEGIN_EXPORTS( "wav" ) END_PAYLOADS BEGIN_FILE_FORMATS - BEGIN_FILE_FORMAT( "Wav", "wav", "audio/x-wav", wav_open, wav_close) + BEGIN_FILE_FORMAT( "Wav", "wav", "audio/x-wav", wav_open, wav_close, wav_mem_open, wav_mem_close) BEGIN_SUBTYPES SUBTYPE( WAV_PCM, "Pcm16", 8000, 1, CODEC_PCM16 ) // we only support 8000/1 channel ! SUBTYPE( WAV_ALAW, "A-Law", 8000, 1, CODEC_ALAW ) diff --git a/core/plug-in/wav/wav_hdr.c b/core/plug-in/wav/wav_hdr.c index a5ebde36..c036e5be 100644 --- a/core/plug-in/wav/wav_hdr.c +++ b/core/plug-in/wav/wav_hdr.c @@ -206,7 +206,110 @@ int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_c return 0; } +#define SAFE_MEM_READ(buf,s,mptr,pos,size) \ + if (*pos+s>size) return -1; \ + memcpy(buf,mptr+*pos,s); \ + *pos+=s; + +int wav_mem_open(unsigned char* mptr, unsigned long size, unsigned long* pos, + struct amci_file_desc_t* fmt_desc, int options, long h_codec) { + if(options == AMCI_RDONLY){ + + char tag[4]={'\0'}; + unsigned int file_size=0; + unsigned int chunk_size=0; + unsigned short fmt=0; + unsigned short channels=0; + unsigned int rate=0; + unsigned short bits_per_sample=0; + unsigned short sample_size=0; + + if(!mptr) + return -1; + *pos=0; + + DBG("trying to read WAV file from memory\n"); + + SAFE_MEM_READ(tag,4,mptr,pos,size); + DBG("tag = <%.4s>\n",tag); + if(strncmp(tag,"RIFF",4)){ + DBG("wrong format !"); + return -1; + } + + SAFE_MEM_READ(&file_size,4,mptr,pos,size); + DBG("file size = <%u>\n",file_size); + + SAFE_MEM_READ(tag,4,mptr,pos,size); + DBG("tag = <%.4s>\n",tag); + if(strncmp(tag,"WAVE",4)){ + DBG("wrong format !"); + return -1; + } + + SAFE_MEM_READ(tag,4,mptr,pos,size); + DBG("tag = <%.4s>\n",tag); + if(strncmp(tag,"fmt ",4)){ + DBG("wrong format !"); + return -1; + } + + SAFE_MEM_READ(&chunk_size,4,mptr,pos,size); + DBG("chunk_size = <%u>\n",chunk_size); + + SAFE_MEM_READ(&fmt,2,mptr,pos,size); + DBG("fmt = <%.2x>\n",fmt); + + SAFE_MEM_READ(&channels,2,mptr,pos,size); + DBG("channels = <%i>\n",channels); + + SAFE_MEM_READ(&rate,4,mptr,pos,size); + DBG("rate = <%i>\n",rate); + + /* do not read bytes/sec and block align */ + *pos +=6; + + SAFE_MEM_READ(&bits_per_sample,2,mptr,pos,size); + DBG("bits/sample = <%i>\n",bits_per_sample); + + fmt_desc->subtype = fmt; + sample_size = bits_per_sample>>3; + fmt_desc->rate = rate; + fmt_desc->channels = channels; + + if( (fmt == 0x01) && (sample_size == 1)){ + ERROR("Sorry, we don't support PCM 8 bit\n"); + return -1; + } + + *pos+=chunk_size-16; + for(;;) { + + SAFE_MEM_READ(tag,4,mptr,pos,size); + DBG("tag = <%.4s>\n",tag); + + SAFE_MEM_READ(&chunk_size,4,mptr,pos,size); + DBG("chunk size = <%i>\n",chunk_size); + + if(!strncmp(tag,"data",4)) + break; + + *pos += chunk_size; + } + + return 0; + + } else { + ERROR("write support for in-memory file not implemented!\n"); + return -1; + } +} + +int wav_mem_close(unsigned char* mptr, unsigned long* pos, + struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec) { + return 0; +} diff --git a/core/plug-in/wav/wav_hdr.h b/core/plug-in/wav/wav_hdr.h index 06801d3a..840904f9 100644 --- a/core/plug-in/wav/wav_hdr.h +++ b/core/plug-in/wav/wav_hdr.h @@ -33,5 +33,11 @@ int wav_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec); int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec); +int wav_mem_open(unsigned char* mptr, unsigned long size, unsigned long* pos, + struct amci_file_desc_t* fmt_desc, int options, long h_codec); + +int wav_mem_close(unsigned char* mptr, unsigned long* pos, + struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec); + #endif