Index: apps/Makefile =================================================================== --- apps/Makefile (revision 20961) +++ apps/Makefile (working copy) @@ -44,6 +44,10 @@ CFLAGS+=-fPIC endif +ifeq ($(wildcard $(CROSS_COMPILE_TARGET)/usr/lib/libSoundTouch.so $(CROSS_COMPILE_TARGET)/usr/local/lib/libSoundTouch.so),) + MODS:=$(filter-out app_voicechanger.so,$(MODS)) +endif + # If you have UnixODBC you can use ODBC voicemail # storage # @@ -67,6 +71,9 @@ app_rpt.so : app_rpt.o $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -ltonezone +app_voicechanger.so : app_voicechanger.o + $(CC) $(SOLINK) -o $@ ${CYGSOLINK} $< ${CYGSOLIB} -lSoundTouch + install: all for x in $(MODS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done rm -f $(DESTDIR)$(MODULES_DIR)/app_cut.so Index: apps/app_voicechanger.c =================================================================== --- apps/app_voicechanger.c (revision 0) +++ apps/app_voicechanger.c (revision 0) @@ -0,0 +1,433 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Voice Changer Dial App + * + * Copyright (C) 2005, Justin R. Tunney + * + * Justin Tunney + * + * This file is based off of the app_forwardcall.c application written + * by Anthony Minessale II (anthmct@yahoo.com) + * + * Thanks to Claude Patry for his help. + * + * Keep it Open Source Pigs + * + * This program is free software, distributed under the terms of + * the GNU General Public License version 2.0. + */ + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.02 $") + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static char *desc = "Voice Changing Dial Application"; + +static char *tdesc = "\n" +"Usage VoiceChangeDial([|||])\n\n" +"Pitch:\n" +" Negative is lower, positive is higher. Default is -5.0\n" +"Options:\n" +" h -- Hangup if the call was successful.\n" +" r -- Indicate 'ringing' to the caller.\n" +" t -- Thread mode, run both channels in a seperate thread for the bridge.\n\n" +"Example:\n" +" exten => 666,1,VoiceChangeDial(Zap/g3/${THE_PRESIDENT}|-6.0|20|hr)\n"; + +static char *app = "VoiceChangeDial"; +static char *synopsis = "Voice Changing Dial Application"; + +static int ast_check_hangup_locked(struct ast_channel *chan) +{ + int res; + ast_mutex_lock(&chan->lock); + res = ast_check_hangup(chan); + ast_mutex_unlock(&chan->lock); + return res; +} + +#define get_frame_or_break(f,chan) f = ast_read(chan); if (!f) break; +#define clean_frame(f) if(f) {ast_frfree(f); f = NULL;} +#define ALL_DONE(u,ret) {ast_indicate(chan, -1); LOCAL_USER_REMOVE(u) ; return ret;} + +#define ready_to_talk(chan,peer) ((!chan || !peer || ast_check_hangup_locked(chan) || ast_check_hangup_locked(peer)) ? 0 : 1) + +#define DONE_WITH_ERROR -1 +#define RUNNING 1 +#define DONE 0 + +LOCAL_USER_DECL; + +struct threaded_bridge_object { + struct ast_channel *chan; + struct ast_channel *peer; + struct __SoundTouch *snd; +}; + +static void *ast_threaded_bridge_run(void *obj) +{ + struct threaded_bridge_object *tbo; + struct ast_frame *f; + int showedWarning = 0; + int res = 0; + + tbo = obj; + + for (;;) { + res = ast_waitfor(tbo->chan, -1); + if (res < 0) + break; + if (!(f = ast_read(tbo->chan))) + break; + + /* shut asterisk's bitching up. We know it's blocking + * you dolt. now that we only read from ourself and + * write to our partener we dont care about blocking + * since they are executing the same exact code and + * only reading from itself and writing to us. + */ + ast_clear_flag(tbo->peer, AST_FLAG_BLOCKING); + + /* this prevents asterisk from going insane when the + * call hangs up, but blocking is still a bit flaky */ + ast_clear_flag(tbo->chan, AST_FLAG_BLOCKING); + + if (f->frametype != AST_FRAME_VOICE && f->frametype != AST_FRAME_DTMF) { + clean_frame(f); + continue; + } + + /* shall we apply the voice adjustments gentlemen? */ + if (tbo->snd && f->frametype == AST_FRAME_VOICE) { + if (f->subclass != AST_FORMAT_SLINEAR && !showedWarning) { + ast_log(LOG_WARNING, "%s: OMG why isn't the channel using SLINEAR?\n", app); + showedWarning = 1; + } + SoundTouch_putSamples(tbo->snd, f->data, f->samples); + //f->samples = SoundTouch_receiveSamplesEx(tbo->snd, f->data, f->samples); + } + + ast_write(tbo->peer, f); + clean_frame(f); + } + + if (tbo->snd) { + ast_log(LOG_DEBUG, "%s: Freeing SoundTouch object...\n", app); + SoundTouch_destruct(tbo->snd); + } + + return NULL; +} + +static void ast_threaded_bridge_launch(struct threaded_bridge_object *tbo) +{ + pthread_attr_t attr; + int result = 0; + pthread_t thread; + + result = pthread_attr_init(&attr); + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + result = ast_pthread_create(&thread, &attr, ast_threaded_bridge_run, tbo); + result = pthread_attr_destroy(&attr); +} + +static struct __SoundTouch *ast_create_soundtouch(float newPitch) +{ + struct __SoundTouch *snd; + + ast_log(LOG_DEBUG, "%s: Creating SoundTouch object...\n", app); + snd = SoundTouch_construct(); + if (!snd) { + ast_log(LOG_WARNING, "%s: Failed to create SoundTouch object\n", app); + return NULL; + } + /* telephone voice is sampled 8000 times a second with one channel */ + SoundTouch_setChannels(snd, 1); + SoundTouch_setSampleRate(snd, 8000); + SoundTouch_setPitchSemiTonesFloat(snd, newPitch); + //SoundTouch_setSetting(snd, SETTING_USE_QUICKSEEK, 1); + //SoundTouch_setSetting(snd, SETTING_USE_AA_FILTER, 1); + + return snd; +} + +static void ast_threaded_bridge(struct ast_channel *chan, struct ast_channel *peer, float newPitch) +{ + struct threaded_bridge_object chan_o, peer_o; + + chan_o.chan = chan; + chan_o.peer = peer; + + peer_o.chan = peer; + peer_o.peer = chan; + peer_o.snd = NULL; + + ast_threaded_bridge_launch(&peer_o); + ast_threaded_bridge_run(&chan_o); +} + + +/* ast_bridge_audio(chan,peer); + this is a no-nonsense optionless bridge function that probably needs to grow a little. + This function makes no attempt to perform a native bridge or do anything cool because it's + main usage is for situations where you are doing a translated codec in a voip gateway + where you simply want to forward the call elsewhere. + This is my perception of what ast_channel_bridge may have looked like in the beginning ;) +*/ + +static int ast_bridge_audio(struct ast_channel *chan, struct ast_channel *peer, float newPitch) +{ + struct ast_channel *active=NULL, *inactive=NULL; + struct ast_channel *channels[2]; + struct ast_frame *f; + struct __SoundTouch *snd; + + int showedWarning = 0; + int timeout = -1, running = 1; + + snd = ast_create_soundtouch(newPitch); + + channels[0] = chan; + channels[1] = peer; + while (running == RUNNING && (running = ready_to_talk(channels[0], channels[1]))) { + if (!(active = ast_waitfor_n(channels, 2, &timeout))) + continue; + + inactive = active == channels[0] ? channels[1] : channels[0]; + if (!(f = ast_read(active))) { + running = DONE; + break; + } + + /* shall we apply the voice adjustments gentlemen? */ + if (snd && active == chan && f->frametype == AST_FRAME_VOICE) { + if (f->subclass != AST_FORMAT_SLINEAR && !showedWarning) { + ast_log(LOG_WARNING, "%s: OMG why isn't the channel using SLINEAR?\n", app); + showedWarning = 1; + } + SoundTouch_putSamples(snd, f->data, f->samples); + f->samples = SoundTouch_receiveSamplesEx(snd, f->data, f->samples); + } else if (snd && active == chan && f->frametype == AST_FRAME_DTMF) { + if (!f->subclass) { + ast_log(LOG_NOTICE, "no subclass; this frame is undead, undead, undead\n"); + } else { + if (f->subclass == '*') { + newPitch--; + SoundTouch_setPitchSemiTonesFloat(snd, newPitch); + if (option_verbose > 4) + ast_verbose(VERBOSE_PREFIX_4 "pitch is now at (%f)\n", newPitch); + } else if (f->subclass == '#') { + newPitch++; + SoundTouch_setPitchSemiTonesFloat(snd, newPitch); + if (option_verbose > 4) + ast_verbose(VERBOSE_PREFIX_4 "pitch is now at (%f)\n", newPitch); + } + } + } + + if (f->samples) + ast_write(inactive, f); + clean_frame(f); + channels[0] = inactive; + channels[1] = active; + } + + if (snd) { + ast_log(LOG_DEBUG, "%s: Freeing SoundTouch object...\n", app); + SoundTouch_destruct(snd); + } + + return running; +} + + +static int voicechanger_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + char *tech=NULL; + char *dest=NULL, *pi=NULL, *to=NULL, *flags=""; + struct ast_channel *peer; + int state=0, ready=0, timeout=60000, format=chan->nativeformats; + struct ast_frame *f; + float newPitch = -5.0; + + if (!data) { + ast_log(LOG_WARNING, "forwardcall requires an argument\n"); + return -1; + } + LOCAL_USER_ADD(u); + tech = ast_strdupa((char *)data); + if ((pi = strchr(tech, '|'))) { + *pi = '\0'; + pi++; + + if ((to = strchr(pi, '|'))) { + *to = '\0'; + to++; + + if ((flags = strchr(to, '|'))) { + /* i should probably just tokenize at this point */ + *flags = '\0'; + flags++; + } + + if ((timeout = atoi(to))) + timeout *= 1000; + else + timeout = 60000; + } + + newPitch = atof(pi); + } + if ((dest = strchr(tech, '/'))) { + int cause = 0; + *dest = '\0'; + dest++; + + if(!(peer = ast_request(tech, format, dest, &cause))) { + ast_log(LOG_ERROR,"Error creating channel %s/%s\n",tech,dest); + ALL_DONE(u,0); + } + if(flags && strchr(flags,'r')) + ast_indicate(chan, AST_CONTROL_RINGING); + } else { + ast_log(LOG_ERROR,"Error creating channel. invalid name %s\n",tech); + ALL_DONE(u,0); + } + +// ast_log(LOG_NOTICE, "1: Forwarding caller id of %s (%s)\n", chan->cid.cid_name, chan->cid.cid_num); + ast_set_callerid(peer, chan->cid.cid_name, chan->cid.cid_num, chan->cid.cid_num); + + res = ast_call(peer, dest, 0); + if (res < 0) { + ALL_DONE(u,-1); + } + + /* While we haven't timed out and we still have no channel up */ + while (timeout && (peer->_state != AST_STATE_UP)) { + res = ast_waitfor(peer, timeout); + /* Something is not cool */ + if (res < 0) { + break; + } + /* Timed out, so we are done trying */ + if (res == 0) { + break; + } + /* -1 means go forever */ + if (timeout > -1) { + /* res holds the number of milliseconds remaining */ + timeout = res; + if (timeout < 0) { + timeout = 0; + } + } + f = ast_read(peer); + if (f == NULL) { + state = AST_CONTROL_HANGUP; + res = 0; + break; + } + if (f->frametype == AST_FRAME_CONTROL) { + if (f->subclass == AST_CONTROL_RINGING) { + state = f->subclass; + } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) { + state = f->subclass; + ast_frfree(f); + break; + } else if (f->subclass == AST_CONTROL_ANSWER) { + /* This is what we are hoping for */ + state = f->subclass; + ast_frfree(f); + ready=1; + break; + } + /* else who cares lol */ + } + ast_frfree(f); + } + + res = 1; + if (ready && ready_to_talk(chan,peer)) { + if (!ast_channel_make_compatible(chan, peer)) { + ast_answer(chan); + peer->appl = app; + peer->data = ast_strdupa(chan->name); + if (flags && strchr(flags, 'r')) + ast_indicate(chan, -1); + + /* For some reason, this is only working if I do it twice */ +// ast_log(LOG_NOTICE, "2: Forwarding caller id of %s (%s)\n", chan->cid.cid_name, chan->cid.cid_num); + ast_set_callerid(peer, chan->cid.cid_name, chan->cid.cid_num, chan->cid.cid_num); + + /* SoundTouch likes to have SLINEAR input */ + if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) + ast_log(LOG_WARNING, "%s: Unable to set our channel read to linear mode\n", app); + if (ast_set_write_format(peer, AST_FORMAT_SLINEAR) < 0) + ast_log(LOG_WARNING, "%s: Unable to set peer channel write to linear mode\n", app); + + if (flags && strchr(flags, 't')) { + ast_threaded_bridge(chan, peer, newPitch); + } else { + res = ast_bridge_audio(chan, peer, newPitch); + } + } else { + ast_log(LOG_ERROR, "failed to make remote_channel %s/%s Compatible\n", tech, dest); + } + + + } else { + ast_log(LOG_ERROR, "Failed to get remote_channel %s/%s\n", tech, dest); + } + + if (peer) + ast_hangup(peer); + + /* hangup if the call worked and you spec the h flag */ + ALL_DONE(u, (!res && (flags && strchr(flags, 'h'))) ? -1 : 0); +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + return ast_register_application(app, voicechanger_exec, synopsis, tdesc); +} + +const char *description(void) +{ + return desc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +const char *key() +{ + return ASTERISK_GPL_KEY; +} Index: doc/voicechanger.txt =================================================================== --- doc/voicechanger.txt (revision 0) +++ doc/voicechanger.txt (revision 0) @@ -0,0 +1,34 @@ +The Asterisk Voice Changer Dial Application +====================================================================== + +To get the voice changer module to compile, you need to install a +hacked version of SoundTouch, which is a library that provides nifty +features such as buffered pitch adjustment. + +ASTSOURCES=/usr/src/asterisk +cd /usr/src +wget http://www.hut.fi/u/oparviai/soundtouch_v1.3.0.zip +unzip soundtouch_v1.3.0.zip +cd SoundTouch-1.3.0 +patch -p1 <$ASTSOURCES/contrib/SoundTouch-1.3.0-C_FrontEnd-0.1.patch +./configure && make && make install +cd source/SoundTouch/ +g++ -shared -fPIC -o libSoundTouch.so *.o +cp libSoundTouch.so /usr/lib + +Once you've successfully completed the above steps, run 'make' against +Asterisk and it should compile the app_voicechanger.so module. + +For help on usage, go to the Asterisk CLI and type: + +*CLI> show application VoiceChangeDial + +If the voicechanger module fails to load or compile, you may want to +try running: + +ldconfig -v + +And if it still doesn't work: + +echo /usr/local/lib >>/etc/ld.so.conf +ldconfig -v Index: contrib/SoundTouch-1.3.0-C_FrontEnd-0.1.patch =================================================================== --- contrib/SoundTouch-1.3.0-C_FrontEnd-0.1.patch (revision 0) +++ contrib/SoundTouch-1.3.0-C_FrontEnd-0.1.patch (revision 0) @@ -0,0 +1,212 @@ +diff -urN SoundTouch-1.3.0-base/include/SoundTouch.h SoundTouch-1.3.0/include/SoundTouch.h +--- SoundTouch-1.3.0-base/include/SoundTouch.h 2004-11-13 11:37:36.000000000 -0500 ++++ SoundTouch-1.3.0/include/SoundTouch.h 2005-11-08 17:50:37.000000000 -0500 +@@ -72,11 +72,15 @@ + #ifndef SoundTouch_H + #define SoundTouch_H + ++#if defined(__cplusplus) || defined(c_plusplus) + #include "FIFOSamplePipe.h" ++#endif + #include "STTypes.h" + ++#if defined(__cplusplus) || defined(c_plusplus) + namespace soundtouch + { ++#endif + + /// Soundtouch library version string + #define SOUNDTOUCH_VERSION "1.3.0" +@@ -116,6 +120,7 @@ + #define SETTING_OVERLAP_MS 5 + + ++#if defined(__cplusplus) || defined(c_plusplus) + class SoundTouch : public FIFOProcessor + { + private: +@@ -248,5 +253,39 @@ + /// - clear() : Clears all samples from ready/processing buffers. + }; + ++extern "C" { ++#endif /* #ifdef __cplusplus */ ++ ++ /* C Front End Functions for SoundTouch class */ ++ struct __SoundTouch; ++ struct __SoundTouch *SoundTouch_construct(); ++ void SoundTouch_destruct(struct __SoundTouch *st); ++ void SoundTouch_setRate(struct __SoundTouch *st, float newRate); ++ void SoundTouch_setTempo(struct __SoundTouch *st, float newTempo); ++ void SoundTouch_setRateChange(struct __SoundTouch *st, float newRate); ++ void SoundTouch_setTempoChange(struct __SoundTouch *st, float newTempo); ++ void SoundTouch_setPitch(struct __SoundTouch *st, float newPitch); ++ void SoundTouch_setPitchOctaves(struct __SoundTouch *st, float newPitch); ++ void SoundTouch_setPitchSemiTonesInt(struct __SoundTouch *st, int newPitch); ++ void SoundTouch_setPitchSemiTonesFloat(struct __SoundTouch *st, float newPitch); ++ void SoundTouch_setChannels(struct __SoundTouch *st, uint numChannels); ++ void SoundTouch_setSampleRate(struct __SoundTouch *st, uint srate); ++ void SoundTouch_flush(struct __SoundTouch *st); ++ void SoundTouch_putSamples(struct __SoundTouch *st, SAMPLETYPE *samples, uint numSamples); ++ void SoundTouch_clear(struct __SoundTouch *st); ++ int SoundTouch_setSetting(struct __SoundTouch *st, uint settingId, uint value); ++ uint SoundTouch_getSetting(struct __SoundTouch *st, uint settingId); ++ uint SoundTouch_numUnprocessedSamples(struct __SoundTouch *st); ++ ++ uint SoundTouch_receiveSamplesEx(struct __SoundTouch *st, SAMPLETYPE *output, uint maxSamples); ++ uint SoundTouch_receiveSamples(struct __SoundTouch *st, uint maxSamples); ++ uint SoundTouch_numSamples(struct __SoundTouch *st); ++ int SoundTouch_isEmpty(struct __SoundTouch *st); ++ ++#if defined(__cplusplus) || defined(c_plusplus) + } ++ ++} ++#endif ++ + #endif +diff -urN SoundTouch-1.3.0-base/include/STTypes.h SoundTouch-1.3.0/include/STTypes.h +--- SoundTouch-1.3.0-base/include/STTypes.h 2004-10-30 11:10:16.000000000 -0400 ++++ SoundTouch-1.3.0/include/STTypes.h 2005-11-08 17:50:37.000000000 -0500 +@@ -52,9 +52,10 @@ + + #endif // _WINDEF_ + +- ++#if defined(__cplusplus) || defined(c_plusplus) + namespace soundtouch + { ++#endif + /// Enable one of the following defines to choose either 16bit integer or + /// 32bit float sample type. If you don't have opinion, using integer samples + /// is generally faster. +@@ -105,6 +106,8 @@ + #endif + + #endif // INTEGER_SAMPLES ++#if defined(__cplusplus) || defined(c_plusplus) + }; ++#endif + + #endif +diff -urN SoundTouch-1.3.0-base/source/SoundTouch/SoundTouch.cpp SoundTouch-1.3.0/source/SoundTouch/SoundTouch.cpp +--- SoundTouch-1.3.0-base/source/SoundTouch/SoundTouch.cpp 2005-02-06 07:25:50.000000000 -0500 ++++ SoundTouch-1.3.0/source/SoundTouch/SoundTouch.cpp 2005-11-08 17:50:37.000000000 -0500 +@@ -470,3 +470,117 @@ + } + return 0; + } ++ ++ /***************************************/ ++ /***** CRAZY C FRONT END FUNCTIONS *****/ ++/***************************************/ ++ ++extern "C" struct __SoundTouch *SoundTouch_construct() ++{ ++ return (struct __SoundTouch *)(new SoundTouch()); ++} ++ ++extern "C" void SoundTouch_destruct(struct __SoundTouch *st) ++{ ++ delete (SoundTouch *)st; ++} ++ ++extern "C" void SoundTouch_setRate(struct __SoundTouch *st, float newRate) ++{ ++ ((SoundTouch *)st)->setRate(newRate); ++} ++ ++extern "C" void SoundTouch_setTempo(struct __SoundTouch *st, float newTempo) ++{ ++ ((SoundTouch *)st)->setTempo(newTempo); ++} ++ ++extern "C" void SoundTouch_setRateChange(struct __SoundTouch *st, float newRate) ++{ ++ ((SoundTouch *)st)->setRateChange(newRate); ++} ++ ++extern "C" void SoundTouch_setTempoChange(struct __SoundTouch *st, float newTempo) ++{ ++ ((SoundTouch *)st)->setTempoChange(newTempo); ++} ++ ++extern "C" void SoundTouch_setPitch(struct __SoundTouch *st, float newPitch) ++{ ++ ((SoundTouch *)st)->setPitch(newPitch); ++} ++ ++extern "C" void SoundTouch_setPitchOctaves(struct __SoundTouch *st, float newPitch) ++{ ++ ((SoundTouch *)st)->setPitchOctaves(newPitch); ++} ++ ++extern "C" void SoundTouch_setPitchSemiTonesInt(struct __SoundTouch *st, int newPitch) ++{ ++ ((SoundTouch *)st)->setPitchSemiTones(newPitch); ++} ++ ++extern "C" void SoundTouch_setPitchSemiTonesFloat(struct __SoundTouch *st, float newPitch) ++{ ++ ((SoundTouch *)st)->setPitchSemiTones(newPitch); ++} ++ ++extern "C" void SoundTouch_setChannels(struct __SoundTouch *st, uint numChannels) ++{ ++ ((SoundTouch *)st)->setChannels(numChannels); ++} ++ ++extern "C" void SoundTouch_setSampleRate(struct __SoundTouch *st, uint srate) ++{ ++ ((SoundTouch *)st)->setSampleRate(srate); ++} ++ ++extern "C" void SoundTouch_flush(struct __SoundTouch *st) ++{ ++ ((SoundTouch *)st)->flush(); ++} ++ ++extern "C" void SoundTouch_putSamples(struct __SoundTouch *st, SAMPLETYPE *samples, uint numSamples) ++{ ++ ((SoundTouch *)st)->putSamples(samples, numSamples); ++} ++ ++extern "C" void SoundTouch_clear(struct __SoundTouch *st) ++{ ++ ((SoundTouch *)st)->clear(); ++} ++ ++extern "C" int SoundTouch_setSetting(struct __SoundTouch *st, uint settingId, uint value) ++{ ++ return ((SoundTouch *)st)->setSetting(settingId, value); ++} ++ ++extern "C" uint SoundTouch_getSetting(struct __SoundTouch *st, uint settingId) ++{ ++ return ((SoundTouch *)st)->getSetting(settingId); ++} ++ ++extern "C" uint SoundTouch_numUnprocessedSamples(struct __SoundTouch *st) ++{ ++ return ((SoundTouch *)st)->numUnprocessedSamples(); ++} ++ ++extern "C" uint SoundTouch_receiveSamplesEx(struct __SoundTouch *st, SAMPLETYPE *output, uint maxSamples) ++{ ++ return ((SoundTouch *)st)->receiveSamples(output, maxSamples); ++} ++ ++extern "C" uint SoundTouch_receiveSamples(struct __SoundTouch *st, uint maxSamples) ++{ ++ return ((SoundTouch *)st)->receiveSamples(maxSamples); ++} ++ ++extern "C" uint SoundTouch_numSamples(struct __SoundTouch *st) ++{ ++ return ((SoundTouch *)st)->numSamples(); ++} ++ ++extern "C" int SoundTouch_isEmpty(struct __SoundTouch *st) ++{ ++ return ((SoundTouch *)st)->isEmpty(); ++}