Click here to Skip to main content
15,885,366 members
Articles / Mobile Apps / iPhone

ESpeakEngine - Objective-C speech synthesizer

Rate me:
Please Sign up or sign in to vote.
4.80/5 (3 votes)
23 Jan 2012BSD2 min read 74K   3K   22  
ESpeakEngine - Objective-C speech synthesizer
  • ESpeakTest.zip
    • __MACOSX
    • ESpeakTest
      • .DS_Store
      • .git
        • branches
        • COMMIT_EDITMSG
        • config
        • description
        • HEAD
        • hooks
          • applypatch-msg.sample
          • commit-msg.sample
          • post-commit.sample
          • post-receive.sample
          • post-update.sample
          • pre-applypatch.sample
          • pre-commit.sample
          • prepare-commit-msg.sample
          • pre-rebase.sample
          • update.sample
        • index
        • info
          • exclude
        • logs
          • HEAD
          • refs
            • heads
              • master
        • objects
          • 00
            • e88867c94b8f6728422a4ad099a53a9f392cb7
          • 01
            • 477be5465c04d4cfd46ab95003f17445731d74
          • 02
            • 37597b848a1890bb30ba0ff4102f8107cafdcb
            • 6363f6a3bb9fe78b03b09f4d0fdce472b95e20
            • 69a98a29eb1baf79dd1f6f2bbc9370b9c4c9cb
            • 78ea2136d97866104e3789d7950d39632b5046
          • 03
            • 7b0f820ace631b85715dfb13cc21fe26daa44d
            • dac4f6baff6f5a2b06f5a68b6daa265c16e0a2
            • edde41daf54530ac1fe426e9349a5544b48ccc
          • 05
            • b2d0d21b687b4190600f6ea0206972c960dd1c
          • 06
            • 23379b22e1417ed806ff4c48337afbe9457cdf
          • 0c
            • 2d13a65548266d9c8a6758599c9a86faf53b28
          • 0d
            • 51695daffab058d065e942018f5c56067942fe
            • 6fa2a91273ee3271fb1d3202d7f6ad86379ef7
          • 10
            • e91b22ea1e6c67962cd858e8d03de74e7abf6f
          • 11
            • 8095eb5859b9c2ee14be00adeb87ee66eaec94
          • 12
            • 83826f9250bed7326aed2542a0e3c5b47edbec
            • ce1096a4ef6c4538a43ebd951dcaf2b624b717
          • 13
            • 664a34fe6a3b585603e00df15b17bcef3591e0
            • 93b3551c88468dccede7550726aae27a931555
          • 14
            • 1160885f6955ee4855933ce984bc117649d220
            • 417c1fdfdb3af16ef717d90a078f5c9b8468c3
          • 15
            • 96e3c77205e1688ad5968359106b5ea92d322a
          • 17
            • 004519410f433308228397abc30d2b69e55b26
          • 19
            • 3700525eaa0aa02256a4211c820007bb005995
            • da34a5b9b6e585ece12d4a6ec24341fa98f50c
            • fd69d3bec6857b0d4b29f4f26c03ac0dc727e3
          • 1a
            • 61238c5c772724cb12490eb555630bcc0e7870
            • 9e53b6e584bd1e8245f3fc69751211b7d4e86a
          • 1c
            • 2992dd5455746aef85905cc0293031e3e9ceaa
            • a6f6abadb2e86be2baf2f823a27d16c43d2528
          • 1e
            • 9a757f891fd7ece2ff350dc9225837cb7e70f7
          • 1f
            • 2eb929019174a9b4c2654c1398ed26aca50e2c
          • 20
            • 5e59c21f973ea13de6deab139efee2f171027a
          • 21
            • 4d6f9d708a7691c1abf13b0801ee9a54a44ae6
          • 22
            • a3b80f6954fd663b59f66210e7b658c5db3a3f
            • a95d18a3ead5d0274abab7986121dccafc1842
          • 23
            • 8c69126e18387f9c2b3d059db6d7df1cd91a6f
          • 25
            • 55d5d8bec5ee0524034d96ea5aa95eeb1a90dd
            • 5fe35ca3bf7c8743abf0f95518aae822aac4f0
            • bfdbf47ea384bd8118ed197a8842cb2104eb2a
            • d78c05ca841d2019f204184927282dc69ebd61
          • 28
            • 59e093f75f1f88ca412e0bde9345afc01f15ac
          • 29
            • 91e99d136fe8e13f7c0c99eaa27c8ca397186d
          • 2a
            • 736d4d3f3c3ffe36bea620d6c28b672efc5867
            • fb1adc71b7f4e90bd02355dd8cdb75e8d23870
          • 2e
            • 1534872410e5819a69396cdbb57225baa3a888
            • 9dd1f58881b69e931f3b5a73fef461eb9b838b
          • 30
            • 441d7e239ccd9169d15b04efc59be7a158a4a5
            • dea89200864b8e84dc0fec67850491d460a78d
          • 31
            • 02c9a1a9a5db99cadf90502de610f69c994913
            • bd479a3d5873c8a970de821a84d2addc864839
          • 32
            • 49dd4f8012e94af03784f043478ebab38fd0b7
          • 33
            • 00c360065de4a18e093dd02df3f6419b656f74
            • 430a22c186f11e5cf838e81dacb386a7e4c8d0
            • 8b8c450f3027ea5d3751fc1e0b4bf969cc1afc
          • 35
            • a4a3fc517a71ec8bad8f9d0c71bd9da960c494
          • 36
            • 665314753a98e11162485805608fe1aca7bc1e
            • 669d3f0592eaeb8465b81341b486a4c9c1eb11
            • a4bff6503dfb82fcdc7a160cc36f4cdd9e333d
          • 3a
            • f2aa1af75587b7a29685fa2dbbebbd2b3592c2
          • 3b
            • 105a7fb0f9b3bb28a87a7212dfcef08daf53c0
          • 3d
            • d75260a68d0fb7b88fe70d0e3e73c916b963e5
          • 3e
            • 520ec5c8b88db8f95f6a230273278fd39bfbd6
          • 41
            • 22d96b39bf2bc1025a1c57ef01b8b03936773d
          • 42
            • dbdc5586bc89d681d23851f4a5aafd79b414cb
            • de58882d0c80e94597b575893afc8e99bd431e
          • 46
            • 07dd079dd2748f8e9cde034268e6cd68278644
            • 317618d912082d6070e4e3972809824bd35395
          • 47
            • 7b28ff8f86a3158a71c4934fbd3a2456717d7a
          • 4b
            • 6a9ae550599ea85d77fea7c0f71b4d5aaf1ba8
          • 4c
            • 6239268d21312d311a504ad0d8aeb0e4f1030b
          • 4d
            • ec15997c4e00c7a764156687431ac8c47676a5
          • 4e
            • 2b9d23e84059b93883e1a0c0f7a859a23b087e
          • 4f
            • 1904e5164410f93689beb55d46901b214271fe
            • 8f5e88f436d478b126c5c4eccf3568e398c26f
            • e4188e53b10cc21b50c3bf47e9ef3b2fc4c641
          • 50
            • 1b5a4a8620d5bd9a545c8941cdcbd565fca1c6
          • 52
            • 1be164ce6c87e1d6df58fd82ab160c8f6255db
            • 692c38546eb82aee2a7550c93798f70f02dc9a
            • c5ac93561331143a9caea14d6c0f008216b4e9
          • 53
            • 6957cb8fe03bde580784e6f97537ec3444e9c3
            • 7beb3ba82da8af147f028685e61fc839cad713
            • c2a70482993f53d6df321687b5cc4d9e95abc1
            • cb31446e077a5f159c831e126a0e3f9a2d0ed2
          • 56
            • 9f9d05432267a13b75ca2562beb85e1e1c17db
          • 58
            • 1cd883fed6aa3b84b580a543b6ec8998f4d327
          • 5a
            • 24e11ad4dc2842c79033ad323f02e2e6c2f566
            • 85640ee385afef9b9dc9b7d2889d47b81aa1bd
          • 5c
            • 3583da4700ab6982766d187d1195b37f9a3fb5
          • 5e
            • bb6a35716f489f249db8bb0e9df7dde150eba4
          • 5f
            • 3297d3a2009a6051a8ebc606bc674056ea03fc
          • 61
            • 124ff298a7d392b816cb1a71095ed1ec8ffe6b
          • 65
            • 3c3f5c4a2d2e44b8a188b88b64278085ebde27
          • 68
            • 1cad9627cb3af687a30507f05114c89ef9340f
          • 69
            • cceefb779a5236074db6871d7523d92f8a709f
          • 6a
            • 8d5efd87553a3f0e977636c6b819cddf3a99e7
            • cadba6aea97cd920745428a1c4ccd998581cc7
          • 6c
            • 65e3c6851f204d9c4cd9b616b46a9ff425b3c2
          • 6d
            • 826477b6ee1ecb3e502ffe3c26387d1bce3961
          • 6e
            • 11c93121ab5d535e4f2d50253ee4a527694a9f
          • 71
            • 99341c34f93f5fa5219ff479e82edaee5d7936
            • ecab7197ec9646efdae05bb02b465f5b5e361a
          • 73
            • ac62a4ab12374bbf6f72539b2e104d10d7d394
          • 74
            • 00c07a5c17fbbead0d252a22f1fcdb6e5f15c4
            • 0601d129aa08fd59be839301c923b684361dbe
          • 79
            • 2d8a9f9ef248c4358c36000722ba0c53a76497
          • 7c
            • bdab338114c51e83e0b54c67280b91872211d5
          • 7d
            • 276eb2b779d73c46d8ed97e4be287bec96c828
          • 7e
            • 6c16a2c28e97392d20d4f4c243ecd6f6f40a91
            • f93a5edd61fddc97d982242d7654e5ab07a09e
          • 7f
            • 4631899e208f50b855ad579b726eadea70f67d
            • fccbe6dd68c968e78da5b6265a13c62c1fc639
          • 82
            • 2c9a312addee71797811c17690f2be8746bea4
            • 98f98722e5f5f405e9631eb4a5064d87114424
          • 83
            • 71a46410d32c3f000db4c7b11254f48a3d6055
          • 84
            • 79e658ebe74cdd0b9525a41dededf8b9839858
            • ccc3a6d4681e19dbe6b982ddcee2e760944d55
          • 85
            • ebb03e3089c5055e4f76d272866738a90a7842
          • 88
            • 1634035cad7fbc213a9def0b5e5ef7f15dffa2
            • 48d6820e826b907349234a642535725247f837
          • 89
            • 28f0ff69aa677f7c5f96053ca70589552e17b0
            • e6c82914aa9457a644d5a498fa643f98b9ade3
          • 8f
            • c65d4bab0f132f34e70868a961188deabc55dc
            • d4a63a1a24c8b25eab5ed28c135457a8332e6b
          • 92
            • 3d517415d489cc9b3f91638c14264dd0df55a3
            • a1582817dc2f8256db5a02bed320a05f6e5e43
          • 95
            • 8799c61770d05ba341183cd2d6a107a1ec093c
          • 96
            • 35ac150af1804b398d67cf4703d718a16806a7
          • 97
            • 8ec49ed00c46862d89580efe68f7efd98c93c3
            • a337d34bb9ab89812b5e79c3bc2bb784d48953
          • 98
            • 9f9eab7b5ee98f4b6acf35fe8b4ef86db3a62e
          • 9b
            • 06e0bd24aa4658ed8009be3e2fe7e32ccee54e
            • 280bf8bc106ca904c9b33a90d0822c4c9b03fd
            • 2d891f12030afadc1e737914a1759ab59ef01f
            • a872a49ab896e3d7c6203bedfd2502d8cb6521
            • c60c7cbd38db3307551ae17ef2a8a5d623b3a0
          • 9d
            • e1630d90e22bf6df53a0093c212e9e1d7da9b6
            • eba8432350a07d0ab15189bb124d48b836fb62
          • 9e
            • 9c4e7476f3dce3b6808b0c8ea917a4d8503d7f
          • a3
            • 72fb170d6218ccc7298335f1ea55ff674fb3cc
          • a5
            • 4cb02cd26c7ecc2bd08debb1cfe85c3546c039
            • 504505ef08ce6040091e48eb5c1653a34574b0
          • a6
            • 56d2c7f2e9509fef687b3174282f6ec9b61498
            • e0f46bb57f876255cdb5767729aefccbe3a33e
          • a7
            • a8223dbda4d4cd47ae8796ad2be9bc70e46754
          • a9
            • 09e1af21f9dc9dccf095d02920266ea11fe7f5
            • 0c7b72565da6e015014e343f10ae50197587c9
          • aa
            • 80edaad05f0f16c6195e72a0130d803b58bcbc
          • ae
            • 76a4c4deb77d53e7ee512c3e85f45ba802dd08
            • 9247d41055f6721d3500645539ee00ae29d7e4
            • a3d895c09d5eae5411aec5fb5ecbe82451bd50
          • b0
            • d4979c857d151ef5cd27248926fb112c0c3cee
          • b1
            • a874be65ad7df189c933f4c576475f29d15f67
          • b2
            • 7a8114a03ed27348a40f37428107e02f67acc8
            • fd9d084c6df70da314916ee674421872f9ef50
          • b3
            • 2b6a6660b12a14fc5e8749eff9ba696b7f6061
          • b4
            • 8b1788b979853cdd57bb6cec6b9cc4dd7925bc
          • b7
            • 39a86e488eeec6f5693b7ee2590f84d6c95191
          • b8
            • 519559d3db834193518d4f4c4fed777734c8ee
            • 6f59306a576f4548ff3bd62e344570cc538932
            • f782946f4728a629eda8038e82dd165dadee1d
          • ba
            • 7c42cc48ae095ccfffeb03cf36d519d7cbe5a3
          • bc
            • bb2a0058b7866064b779ae246244c5342a0ccb
          • bd
            • 336a9884877889e2f85ee1849f8a068d2c6279
          • be
            • 1b6246a01d3d9c01dac0f888454369e0b32892
          • c0
            • a5475ece89c3274e62f9132560ec8afdd569d4
          • c2
            • 34f46877a761d5c98fda2cbbeb172dc2efc81a
            • 76bec0dd56b68a8e0ed0062b38dd149422b45d
          • c3
            • 2db3968df0779b3c3a47899085625a7b86339b
          • c6
            • 32e263c2a639311be4ca1512aadee97a91bc9a
          • cb
            • c4fb3a5d3dcc07f26b5ca02eb53a52b897aa80
          • cd
            • 02abedb89d8975a792d73de536d2ce82260553
          • ce
            • 800f70be34d9fba11f7d09b0c329b3f259507b
          • cf
            • 584b7dd3f005be3cffb47d27f1bfe980a6e53b
            • f13bac9dd46f2c2a2fb66db64ce78caffd5b5a
          • d0
            • 2b035cae9b12c66a5e0d5ae9cf57433041ed40
            • 864f3d6209ad6f3e8ab51dc880836e67c96fc5
            • b729579de2537a63cebdb809e8bdfb5a897a33
          • d2
            • 5865608d7d973e77c77d13bbdbe3ae454d864d
          • d3
            • d7720074a2fc7e3bfbc18cc1bacad0f9ea61d2
          • d5
            • 06e7f9f85667d054794f808f9098b2a1625372
            • 9fe79529970f627e83857f9bd516f5474bf685
          • d6
            • 737d787752a799349095b49cf81024dfd00fb6
            • 811d3ae4f710d3eb8a16c690cd42bebc4faf4e
          • d7
            • 59cfdf018f8814d995588d176dadde0820b5ca
          • d8
            • ecd252c04bc00a6dcc6d0b84d64285726b65d4
            • f35efaa2dc2293277365d811c9b6222cfe59b8
          • da
            • c1e4d0643b01e99052f575b5f6add2e10a2143
            • ef5160c9c1500d3a9f6b0eadf434ef702a009f
          • db
            • 63d6ca3d87629e693327a2ba4a719e79847b6c
            • de212341a0b131224b3e123f91d154348070ba
          • dc
            • 51396ce24aad46d1c761e2c96f8aee68d4b622
          • de
            • 4786c94cfa90c84982ca06a2a9623035993dfc
          • df
            • 70f4387ca97d30c66d7dc87d5abca9b0ffa820
            • 7fa77bc23477530674f05f4f619803983d5eb0
          • e0
            • 783ec3b1e38ce0f77b5b018d9b8641a7b924c3
            • ea6d6394ab8409463adfeb753f1632ef09a47f
          • e4
            • 16c6dc5e784243ad8d33000139178fa178de87
            • 1d3105c048e789fe605c780efe2881df833ea8
          • e7
            • 178e4d5666a3b75aa48de33b966f0590665a8e
          • e8
            • 5978c76abd7430436356cd2a126018642d6a2d
          • ea
            • dd707322e08d90e3a2208aef9060706c340799
          • eb
            • d92ffb3ec9499292d1faa7ca60adcc5edac74f
          • ed
            • 05f4240da98a569e3c9f9a5b9e2301d7caa79e
          • ee
            • 3bd233e2bc458aab63eb36f613f853f72e6bf7
          • f2
            • 033dc11fee0a1db4a2cae358c1a808149a5aa2
            • 130ba4f980783f8605eb50387ffc2e54f66b3c
          • f3
            • 10f868efea6408fc49fd4e6d6c293db2098114
            • e97b52355ea6f5384790bb3cda677222be900c
          • f4
            • 3ef84fa6caf08bc6e9e442e15d71eaa8c6962a
            • ede3296c4fb294a58715e61ecef26f74ed898a
          • f5
            • 0e89c905a3e8dd519f415585283757fc85d2d6
          • f9
            • 624b31623b500b57b74e9765ac7a2d9f039c65
          • fa
            • 4eece0b1cc7dababed3d09d560f38e00ea8755
          • fc
            • 377156b487a559efb384ec2b6e551d6c2085c6
            • 60f41672c8fb6fc69c1548552988f046a2aa14
          • fe
            • 7c4d8d002499fbaff530adfaa0543d626536dd
          • ff
            • a94206372e46914dcadeb10c212a9674daf62e
          • info
          • pack
        • refs
          • heads
            • master
          • tags
      • ESpeakTest.xcodeproj
        • project.pbxproj
        • project.xcworkspace
          • contents.xcworkspacedata
          • xcuserdata
            • jozefbozek.xcuserdatad
              • UserInterfaceState.xcuserstate
        • xcuserdata
      • ESpeakTest
      • ESpeakTestTests
  • eSpeak_1.0.zip
/***************************************************************************
 *   Copyright (C) 2007, Gilles Casse <gcasse@oralux.org>                  *
 *   based on AudioIO.cc (Audacity-1.2.4b) and wavegen.cpp                 *
 *                                                                         *
 *   This program 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 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 "speech.h"

#ifdef USE_ASYNC
// This source file is only used for asynchronious modes

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include <sys/time.h>
#include <time.h>

#include "portaudio.h"
#ifndef PLATFORM_WINDOWS
#include <unistd.h>
#endif
#include "wave.h"
#include "debug.h"

//<Definitions

enum {ONE_BILLION=1000000000};

#ifdef USE_PORTAUDIO

#undef USE_PORTAUDIO
// determine portaudio version by looking for a #define which is not in V18
#ifdef paNeverDropInput
#define USE_PORTAUDIO   19
#else
#define USE_PORTAUDIO   18
#endif


static t_wave_callback* my_callback_is_output_enabled=NULL;

#define N_WAV_BUF   10
#define MAX_SAMPLE_RATE 22050
#define FRAMES_PER_BUFFER 512
#define BUFFER_LENGTH (MAX_SAMPLE_RATE*2*sizeof(uint16_t))
//#define THRESHOLD (BUFFER_LENGTH/5)
static char myBuffer[BUFFER_LENGTH];
static char* myRead=NULL; 
static char* myWrite=NULL; 
static int out_channels=1;
static int my_stream_could_start=0;
static int wave_samplerate;

static int mInCallbackFinishedState = false;
#if (USE_PORTAUDIO == 18)
static PortAudioStream *pa_stream=NULL;
#endif
#if (USE_PORTAUDIO == 19)
static struct PaStreamParameters myOutputParameters;
static PaStream *pa_stream=NULL;
#endif

static int userdata[4];
static PaError pa_init_err=0;

// time measurement
// The read and write position audio stream in the audio stream are measured in ms.
// 
// * When the stream is opened, myReadPosition and myWritePosition are cleared.
// * myWritePosition is updated in wave_write.
// * myReadPosition is updated in pa_callback (+ sample delay).

static uint32_t myReadPosition = 0; // in ms
static uint32_t myWritePosition = 0;

//>
//<init_buffer, get_used_mem

static void init_buffer()
{
  myWrite = myBuffer;
  myRead = myBuffer;
  memset(myBuffer,0,BUFFER_LENGTH);
  myReadPosition = myWritePosition = 0;
  SHOW("init_buffer > myRead=0x%x, myWrite=0x%x, BUFFER_LENGTH=0x%x, myReadPosition = myWritePosition = 0\n", (int)myRead, (int)myWrite, BUFFER_LENGTH);
}

static unsigned int get_used_mem()
{
  char* aRead = myRead;
  char* aWrite = myWrite;
  unsigned int used = 0;

  assert ((aRead >= myBuffer)
	  && (aRead <= myBuffer + BUFFER_LENGTH)
	  && (aWrite >= myBuffer)
	  && (aWrite <= myBuffer + BUFFER_LENGTH));

  if (aRead < aWrite)
    {
      used = aWrite - aRead;
    }
  else
    {
      used = aWrite + BUFFER_LENGTH - aRead;
    }
  SHOW("get_used_mem > %d\n", used);

  return used;
}

//>
//<start stream

static void start_stream()
{
  PaError err;
  SHOW_TIME("start_stream");

  my_stream_could_start=0;  
  mInCallbackFinishedState = false;

  err = Pa_StartStream(pa_stream);
  SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err));
  
#if USE_PORTAUDIO == 19
  if(err == paStreamIsNotStopped)
    {
      SHOW_TIME("start_stream > restart stream (begin)");
      // not sure why we need this, but PA v19 seems to need it
      err = Pa_StopStream(pa_stream);
      SHOW("start_stream > Pa_StopStream=%d (%s)\n", err, Pa_GetErrorText(err));
      err = Pa_StartStream(pa_stream);
      SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err));
      SHOW_TIME("start_stream > restart stream (end)");
    }
#endif
}

//>
//<pa_callback

/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
#if USE_PORTAUDIO == 18
static int pa_callback(void *inputBuffer, void *outputBuffer,
			unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
#else
  static int pa_callback(const void *inputBuffer, void *outputBuffer,
			  long unsigned int framesPerBuffer, const PaStreamCallbackTimeInfo *outTime,
			  PaStreamCallbackFlags flags, void *userData )
#endif
{
	int aResult=0; // paContinue
	char* aWrite = myWrite;
	size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer;

	myReadPosition += framesPerBuffer;
	SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n",(int)myReadPosition, framesPerBuffer, n);

	if (aWrite >= myRead)
	{
		if((size_t)(aWrite - myRead) >= n)
		{
			memcpy(outputBuffer, myRead, n);
			myRead += n;
		}
		else
		{
			SHOW_TIME("pa_callback > underflow");
			aResult=1; // paComplete;
			mInCallbackFinishedState = true;
			size_t aUsedMem=0;
			aUsedMem = (size_t)(aWrite - myRead);
			if (aUsedMem)
			{
				memcpy(outputBuffer, myRead, aUsedMem);
			}
			char* p = (char*)outputBuffer + aUsedMem;
			memset(p, 0, n - aUsedMem); 
			//	  myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
			myRead = aWrite;
		}
	}
	else // myRead > aWrite
	{
		if ((size_t)(myBuffer + BUFFER_LENGTH - myRead) >= n)
		{
			memcpy(outputBuffer, myRead, n);
			myRead += n;
		}
		else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n)
		{
			int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
			if (aTopMem)
			{
				SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
				memcpy(outputBuffer, myRead, aTopMem);
			}
			int aRest = n - aTopMem;
			if (aRest)
			{
				SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
				char* p = (char*)outputBuffer + aTopMem;
				memcpy(p, myBuffer, aRest);
			}
			myRead = myBuffer + aRest;
		}
		else
		{ 
			SHOW_TIME("pa_callback > underflow");
			aResult=1; // paComplete;

			int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
			if (aTopMem)
			{
				SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n",(int)myRead, (int)aTopMem);
				memcpy(outputBuffer, myRead, aTopMem);
			}
			int aRest = aWrite - myBuffer;
			if (aRest)
			{
				SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n",(int)myRead, (int)aRest);
				char* p = (char*)outputBuffer + aTopMem;
				memcpy(p, myBuffer, aRest);
			}

			size_t aUsedMem = aTopMem + aRest;
			char* p = (char*)outputBuffer + aUsedMem;
			memset(p, 0, n - aUsedMem); 
			//	  myReadPosition += aUsedMem/(out_channels*sizeof(uint16_t));
			myRead = aWrite;
		}
	}

	SHOW("pa_callback > myRead=%x\n",(int)myRead);


  // #if USE_PORTAUDIO == 18
  //   if(aBufferEmpty)
  //     {
  //       static int end_timer = 0;
  //       if(end_timer == 0)
  // 	end_timer = 4;
  //       if(end_timer > 0)
  // 	{
  // 	  end_timer--;
  // 	  if(end_timer == 0)
  // 	    return(1);
  // 	}
  //     }
  //   return(0);
  // #else

#ifdef ARCH_BIG
	{
		// BIG-ENDIAN, swap the order of bytes in each sound sample in the portaudio buffer
		int c;
		unsigned char *out_ptr;
		unsigned char *out_end;
		out_ptr = (unsigned char *)outputBuffer;
		out_end = out_ptr + framesPerBuffer*2 * out_channels;
		while(out_ptr < out_end)
		{
			c = out_ptr[0];
			out_ptr[0] = out_ptr[1];
			out_ptr[1] = c;
			out_ptr += 2;
		}
	}
#endif


	return(aResult);
  //#endif

}  //  end of WaveCallBack

//>


void wave_flush(void* theHandler)
{
  ENTER("wave_flush");

  if (my_stream_could_start)
    {
//       #define buf 1024
//       static char a_buffer[buf*2];
//       memset(a_buffer,0,buf*2);
//       wave_write(theHandler, a_buffer, buf*2);
      start_stream();
    }
}

//<wave_open_sound

static int wave_open_sound()
{
  ENTER("wave_open_sound");

  PaError err=paNoError;
  PaError active;

#if USE_PORTAUDIO == 18
  active = Pa_StreamActive(pa_stream);
#else
  active = Pa_IsStreamActive(pa_stream);
#endif

  if(active == 1)
    {
      SHOW_TIME("wave_open_sound > already active");
      return(0);
    }
  if(active < 0)
    {
      out_channels = 1;

#if USE_PORTAUDIO == 18
      //      err = Pa_OpenDefaultStream(&pa_stream,0,1,paInt16,wave_samplerate,FRAMES_PER_BUFFER,N_WAV_BUF,pa_callback,(void *)userdata);

   PaDeviceID playbackDevice = Pa_GetDefaultOutputDeviceID();

   PaError err = Pa_OpenStream( &pa_stream,
				/* capture parameters */
				paNoDevice,
				0,
				paInt16,
				NULL,
				/* playback parameters */
				playbackDevice,
				out_channels,
				paInt16,
				NULL,
				/* general parameters */
				wave_samplerate, FRAMES_PER_BUFFER, 0,
				//paClipOff | paDitherOff,
				paNoFlag,
				pa_callback, (void *)userdata);
   
      SHOW("wave_open_sound > Pa_OpenDefaultStream(1): err=%d (%s)\n",err, Pa_GetErrorText(err));      

      if(err == paInvalidChannelCount)
	{
	  SHOW_TIME("wave_open_sound > try stereo");
	  // failed to open with mono, try stereo
	  out_channels = 2;
	  //	  myOutputParameters.channelCount = out_channels;
	  PaError err = Pa_OpenStream( &pa_stream,
				       /* capture parameters */
				       paNoDevice,
				       0,
				       paInt16,
				       NULL,
				       /* playback parameters */
				       playbackDevice,
				       out_channels,
				       paInt16,
				       NULL,
				       /* general parameters */
				       wave_samplerate, FRAMES_PER_BUFFER, 0,
				       //paClipOff | paDitherOff,
				       paNoFlag,
				       pa_callback, (void *)userdata);
// 	  err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,
// 				     wave_samplerate,
// 				     FRAMES_PER_BUFFER,
// 				     N_WAV_BUF,pa_callback,(void *)userdata);
	  SHOW("wave_open_sound > Pa_OpenDefaultStream(2): err=%d (%s)\n",err, Pa_GetErrorText(err));
	  err=0; // avoid warning
	}
   mInCallbackFinishedState = false; // v18 only
#else
      myOutputParameters.channelCount = out_channels;
      unsigned long framesPerBuffer = paFramesPerBufferUnspecified;
      err = Pa_OpenStream(
			  &pa_stream,
			  NULL, /* no input */
			  &myOutputParameters,
			  wave_samplerate,
			  framesPerBuffer,
			  paNoFlag,
			  //			  paClipOff | paDitherOff,
			  pa_callback,
			  (void *)userdata);
      if ((err!=paNoError) 
	  && (err!=paInvalidChannelCount)) //err==paUnanticipatedHostError
	{
	  fprintf(stderr, "wave_open_sound > Pa_OpenStream : err=%d (%s)\n",err,Pa_GetErrorText(err));
	  framesPerBuffer = FRAMES_PER_BUFFER;
	  err = Pa_OpenStream(
			      &pa_stream,
			      NULL, /* no input */
			      &myOutputParameters,
			      wave_samplerate,
			      framesPerBuffer,
			      paNoFlag,
			      //			  paClipOff | paDitherOff,
			      pa_callback,
			      (void *)userdata);
	}
      if(err == paInvalidChannelCount)
	{
	  SHOW_TIME("wave_open_sound > try stereo");
	  // failed to open with mono, try stereo
	  out_channels = 2;
	  myOutputParameters.channelCount = out_channels;
	  err = Pa_OpenStream(
			       &pa_stream,
			       NULL, /* no input */
			       &myOutputParameters,
			       wave_samplerate,
			       framesPerBuffer,
			       paNoFlag,
			       //			       paClipOff | paDitherOff,
			       pa_callback,
			       (void *)userdata);

	  //	  err = Pa_OpenDefaultStream(&pa_stream,0,2,paInt16,(double)wave_samplerate,FRAMES_PER_BUFFER,pa_callback,(void *)userdata);
	}
      mInCallbackFinishedState = false;
#endif
    }

  SHOW("wave_open_sound > %s\n","LEAVE");

  return (err != paNoError);
}

//>
//<select_device

#if (USE_PORTAUDIO == 19)
static void update_output_parameters(int selectedDevice, const PaDeviceInfo *deviceInfo)
{
  //  const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i);
  myOutputParameters.device = selectedDevice;
  //  myOutputParameters.channelCount = pdi->maxOutputChannels;
  myOutputParameters.channelCount = 1;
  myOutputParameters.sampleFormat = paInt16;

  // Latency greater than 100ms for avoiding glitches 
  // (e.g. when moving a window in a graphical desktop)
  //  deviceInfo = Pa_GetDeviceInfo(selectedDevice);
  if (deviceInfo)
    {
      double aLatency = deviceInfo->defaultLowOutputLatency;
      double aCoeff = round(0.100 / aLatency);
//      myOutputParameters.suggestedLatency = aCoeff * aLatency;  // to avoid glitches ?
      myOutputParameters.suggestedLatency =  aLatency;          // for faster response ?
      SHOW("Device=%d, myOutputParameters.suggestedLatency=%f, aCoeff=%f\n",
	   selectedDevice,
	   myOutputParameters.suggestedLatency,
	   aCoeff);
    }
  else
    {
      myOutputParameters.suggestedLatency = (double)0.1; // 100ms
      SHOW("Device=%d, myOutputParameters.suggestedLatency=%f (default)\n",
	   selectedDevice,
	   myOutputParameters.suggestedLatency);
    }
    //pdi->defaultLowOutputLatency;

  myOutputParameters.hostApiSpecificStreamInfo = NULL;
}
#endif

static void select_device(const char* the_api)
{
	ENTER("select_device");

#if (USE_PORTAUDIO == 19)
	int numDevices = Pa_GetDeviceCount();
	if( numDevices < 0 )
	{
		SHOW( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
		assert(0);
	}

	PaDeviceIndex i=0, selectedIndex=0, defaultAlsaIndex=numDevices;
	const PaDeviceInfo *deviceInfo=NULL;
	const PaDeviceInfo *selectedDeviceInfo=NULL;

	if(option_device_number >= 0)
	{
		selectedIndex = option_device_number;
		selectedDeviceInfo = Pa_GetDeviceInfo(selectedIndex);
	}

	if(selectedDeviceInfo == NULL)
	{
		for( i=0; i<numDevices; i++ )
		{
		deviceInfo = Pa_GetDeviceInfo( i );
	
			if (deviceInfo == NULL)
			{
				break;
			}
			const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
	
			if (hostInfo && hostInfo->type == paALSA)
			{ 
				// Check (once) the default output device
				if (defaultAlsaIndex == numDevices)
				{
					defaultAlsaIndex = hostInfo->defaultOutputDevice;
					const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo( defaultAlsaIndex );
					update_output_parameters(defaultAlsaIndex, deviceInfo);
					if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0)
					{
						SHOW( "select_device > ALSA (default), name=%s (#%d)\n", deviceInfo->name, defaultAlsaIndex);		  
						selectedIndex = defaultAlsaIndex;
						selectedDeviceInfo = deviceInfo;
						break;
					}
				}
	
				// if the default output device does not match, 
				// look for the device with the highest number of output channels 
				SHOW( "select_device > ALSA, i=%d (numDevices=%d)\n", i, numDevices);
	
				update_output_parameters(i, deviceInfo);
	
				if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0)
				{
					SHOW( "select_device > ALSA, name=%s (#%d)\n", deviceInfo->name, i);
	
					if (!selectedDeviceInfo
						|| (selectedDeviceInfo->maxOutputChannels < deviceInfo->maxOutputChannels))
					{
						selectedIndex = i;
						selectedDeviceInfo = deviceInfo;
					}
				}
			}
		}
	}

	if (selectedDeviceInfo)
	{
		update_output_parameters(selectedIndex, selectedDeviceInfo);
	}
	else 
	{
		i = Pa_GetDefaultOutputDevice();
		deviceInfo = Pa_GetDeviceInfo( i );
		update_output_parameters(i, deviceInfo);
	}

#endif    
}

//>


// int wave_Close(void* theHandler)
// {
//   SHOW_TIME("WaveCloseSound");

//   //  PaError active;

//   // check whether speaking has finished, and close the stream
//   if(pa_stream != NULL)
//     {
//       Pa_CloseStream(pa_stream);
//       pa_stream = NULL;
//       init_buffer();

//       // #if USE_PORTAUDIO == 18
//       //       active = Pa_StreamActive(pa_stream);
//       // #else
//       //       active = Pa_IsStreamActive(pa_stream);
//       // #endif
//       //       if(active == 0)
//       // 	{
//       // 	  SHOW_TIME("WaveCloseSound > ok, not active");
//       // 	  Pa_CloseStream(pa_stream);
//       // 	  pa_stream = NULL;
//       // 	  return(1);
//       // 	}
//     }
//   return(0);
// }

//<wave_set_callback_is_output_enabled

void wave_set_callback_is_output_enabled(t_wave_callback* cb)
{
  my_callback_is_output_enabled = cb;
}

//>
//<wave_init

// TBD: the arg could be "alsa", "oss",...
void wave_init(int srate)
{
  ENTER("wave_init");
  PaError err;

  pa_stream = NULL;
	wave_samplerate = srate;
  mInCallbackFinishedState = false;
  init_buffer();

  // PortAudio sound output library
  err = Pa_Initialize();
  pa_init_err = err;
  if(err != paNoError)
    {
      SHOW_TIME("wave_init > Failed to initialise the PortAudio sound");
    }
}

//>
//<wave_open

void* wave_open(const char* the_api)
{
  ENTER("wave_open");
  static int once=0;

  // TBD: the_api (e.g. "alsa") is not used at the moment
  // select_device is called once
  if (!once)
    {
      select_device("alsa");
      once=1;
    }
  return((void*)1);
}

//>
//<copyBuffer


static size_t copyBuffer(char* dest, char* src, const size_t theSizeInBytes) 
{ 
	size_t bytes_written = 0;
	unsigned int i = 0;
	uint16_t* a_dest = NULL;
	uint16_t* a_src = NULL;
 
	if ((src != NULL) && dest != NULL)
	{ 
		// copy for one channel (mono)?
		if(out_channels==1)
		{ 
			SHOW("copyBuffer > 1 channel > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes);
			memcpy(dest, src, theSizeInBytes);
			bytes_written = theSizeInBytes;
		}
		else // copy for 2 channels (stereo)
		{
			SHOW("copyBuffer > 2 channels > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes);
			i = 0;
			a_dest = (uint16_t* )dest;
			a_src = (uint16_t* )src;
 
			for(i=0; i<theSizeInBytes/2; i++)
			{
				a_dest[2*i] = a_src[i];
				a_dest[2*i+1] = a_src[i];
			}
			bytes_written = 2*theSizeInBytes;
		} // end if(out_channels==1)
	} // end if ((src != NULL) && dest != NULL)
 
	return bytes_written; 
}

//>
//<wave_write

size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize)
{
	ENTER("wave_write");
	size_t bytes_written = 0;
	// space in ringbuffer for the sample needed: 1x mono channel but 2x for 1 stereo channel
	size_t bytes_to_write = (out_channels==1) ? theSize : theSize*2;
	my_stream_could_start = 0;
 
	if(pa_stream == NULL)
	{
		SHOW_TIME("wave_write > wave_open_sound\n");
		if (0 != wave_open_sound())
		{
			SHOW_TIME("wave_write > wave_open_sound fails!");
			return 0;
		}
		my_stream_could_start=1;
	}
	else if (!wave_is_busy(NULL))
	{
		my_stream_could_start = 1;
	}
	assert(BUFFER_LENGTH >= bytes_to_write);
 
	if (myWrite >= myBuffer + BUFFER_LENGTH)
	{
		myWrite = myBuffer;
	} // end if (myWrite >= myBuffer + BUFFER_LENGTH)
 
	size_t aTotalFreeMem=0;
	char* aRead = myRead;
	SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);
 
	while (1)
	{
		if (my_callback_is_output_enabled && (0==my_callback_is_output_enabled()))
		{
			SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
			return 0;
		}
 
		aRead = myRead;
 
		// write pointer is before read pointer?
		if (myWrite >= aRead)
		{
			aTotalFreeMem = aRead + BUFFER_LENGTH - myWrite;
		}
		else // read pointer is before write pointer!
		{
			aTotalFreeMem = aRead - myWrite;
		} // end if (myWrite >= aRead)
 
		if (aTotalFreeMem>1)
		{
			// -1 because myWrite must be different of aRead
			// otherwise buffer would be considered as empty
			aTotalFreeMem -= 1;
		} // end if (aTotalFreeMem>1)
 
		if (aTotalFreeMem >= bytes_to_write)
		{
			break;
		} // end if (aTotalFreeMem >= bytes_to_write)
		
		//SHOW_TIME("wave_write > wait");
		SHOW("wave_write > wait: aTotalFreeMem=%d\n", aTotalFreeMem);
		SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);
		usleep(10000);
	} // end while (1)
 
	aRead = myRead;
 
	// write pointer is ahead the read pointer?
	if (myWrite >= aRead)
	{
		SHOW_TIME("wave_write > myWrite >= aRead");
		// determine remaining free memory to the end of the ringbuffer
		size_t aFreeMem = myBuffer + BUFFER_LENGTH - myWrite;
		// is enough linear space available (regardless 1 or 2 channels)?
		if (aFreeMem >= bytes_to_write)
		{
			// copy direct - no wrap around at end of ringbuffer needed
			myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize);
		}
		else // not enough linear space available
		{
			// 2 channels (stereo)?
			if (out_channels == 2)
			{
				// copy with wrap around at the end of ringbuffer
				copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem/2);
				myWrite = myBuffer;
				myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem/2, theSize - aFreeMem/2);
			}
			else // 1 channel (mono)
			{
				// copy with wrap around at the end of ringbuffer
				copyBuffer(myWrite, theMono16BitsWaveBuffer, aFreeMem);
				myWrite = myBuffer;
				myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem);
			} // end if (out_channels == 2)
		} // end if (aFreeMem >= bytes_to_write)
	} // if (myWrite >= aRead)
	else // read pointer is ahead the write pointer
	{
		SHOW_TIME("wave_write > myWrite <= aRead");
		myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize);
	} // end if (myWrite >= aRead)
 
	bytes_written = bytes_to_write;
	myWritePosition += theSize/sizeof(uint16_t); // add number of samples
 
	if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER))
	{
		start_stream();
	} // end if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER))
 
	SHOW_TIME("wave_write > LEAVE");
 
	return bytes_written;
}

//>
//<wave_close

int wave_close(void* theHandler)
{
  SHOW_TIME("wave_close > ENTER");

  static int aStopStreamCount = 0;

#if (USE_PORTAUDIO == 19)
  if( pa_stream == NULL )
    {
      SHOW_TIME("wave_close > LEAVE (NULL stream)");
      return 0;
    }
      
  if( Pa_IsStreamStopped( pa_stream ) )
    {
      SHOW_TIME("wave_close > LEAVE (stopped)");
      return 0;
    }
#else
  if( pa_stream == NULL )
    {
      SHOW_TIME("wave_close > LEAVE (NULL stream)");
      return 0;
    }
  
  if( Pa_StreamActive( pa_stream ) == false && mInCallbackFinishedState == false )
    {
      SHOW_TIME("wave_close > LEAVE (not active)");
      return 0;
    }
#endif

  // Avoid race condition by making sure this function only
  // gets called once at a time
  aStopStreamCount++;
  if (aStopStreamCount != 1)
    {
      SHOW_TIME("wave_close > LEAVE (stopStreamCount)");
      return 0;
    }
  
  // Comment from Audacity-1.2.4b adapted to the eSpeak context. 
  //
  // We got here in one of two ways:
  //
  // 1. The calling program calls the espeak_Cancel function and we 
  //    therefore want to stop as quickly as possible.  
  //    So we use AbortStream().  If this is
  //    the case the portaudio stream is still in the Running state
  //    (see PortAudio state machine docs).
  //
  // 2. The callback told PortAudio to stop the stream since it had
  //    reached the end of the selection.  
  //    The event polling thread discovered this by noticing that 
  //    wave_is_busy() returned false.
  //    wave_is_busy() (which calls Pa_GetStreamActive()) will not return
  //    false until all buffers have finished playing, so we can call
  //    AbortStream without losing any samples.  If this is the case
  //    we are in the "callback finished state" (see PortAudio state
  //    machine docs).
  //
  // The moral of the story: We can call AbortStream safely, without
  // losing samples.
  //
  // DMM: This doesn't seem to be true; it seems to be necessary to
  // call StopStream if the callback brought us here, and AbortStream
  // if the user brought us here.
  //
  
#if (USE_PORTAUDIO == 19)
  if (pa_stream)
    {
      Pa_AbortStream( pa_stream );
      SHOW_TIME("wave_close > Pa_AbortStream (end)");

      Pa_CloseStream( pa_stream );
      SHOW_TIME("wave_close > Pa_CloseStream (end)");
      pa_stream = NULL;
      mInCallbackFinishedState = false;
    }
#else
  if (pa_stream)
    {
      if (mInCallbackFinishedState)
	{
	  Pa_StopStream( pa_stream );
	  SHOW_TIME("wave_close > Pa_StopStream (end)");
	}
      else
	{
	  Pa_AbortStream( pa_stream );
	  SHOW_TIME("wave_close > Pa_AbortStream (end)");
	}
      Pa_CloseStream( pa_stream );
      SHOW_TIME("wave_close > Pa_CloseStream (end)");

      pa_stream = NULL;
      mInCallbackFinishedState = false;
    }
#endif
  init_buffer();

  aStopStreamCount = 0; // last action
  SHOW_TIME("wave_close > LEAVE");
  return 0;
}

// int wave_close(void* theHandler)
// {
//   ENTER("wave_close");

//   if(pa_stream != NULL)
//     {
//       PaError err = Pa_AbortStream(pa_stream);
//       SHOW_TIME("wave_close > Pa_AbortStream (end)");
//       SHOW("wave_close Pa_AbortStream > err=%d\n",err);
//       while(1)
// 	{
// 	  PaError active;
// #if USE_PORTAUDIO == 18
// 	  active = Pa_StreamActive(pa_stream);
// #else
// 	  active = Pa_IsStreamActive(pa_stream);
// #endif
// 	  if (active != 1)
// 	    {
// 	      break;
// 	    }
// 	  SHOW("wave_close > active=%d\n",err);
// 	  usleep(10000); /* sleep until playback has finished */
// 	}
//       err = Pa_CloseStream( pa_stream );
//       SHOW_TIME("wave_close > Pa_CloseStream (end)");
//       SHOW("wave_close Pa_CloseStream > err=%d\n",err);
//       pa_stream = NULL;
//       init_buffer();
//     }
//   return 0;
// }

//>
//<wave_is_busy

int wave_is_busy(void* theHandler)
{
  PaError active=0;

  SHOW_TIME("wave_is_busy");

  if (pa_stream)
    {
#if USE_PORTAUDIO == 18
      active = Pa_StreamActive(pa_stream) 
	&& (mInCallbackFinishedState == false);
#else
      active = Pa_IsStreamActive(pa_stream)
	&& (mInCallbackFinishedState == false);
#endif
    }
  
  SHOW("wave_is_busy: %d\n",active);


  return (active==1);
}

//>
//<wave_terminate

void wave_terminate()
{
  ENTER("wave_terminate");

  Pa_Terminate();

}

//>
//<wave_get_read_position, wave_get_write_position, wave_get_remaining_time

uint32_t wave_get_read_position(void* theHandler)
{
  SHOW("wave_get_read_position > myReadPosition=%u\n", myReadPosition);
  return myReadPosition;
}

uint32_t wave_get_write_position(void* theHandler)
{
  SHOW("wave_get_write_position > myWritePosition=%u\n", myWritePosition);
  return myWritePosition;
}

int wave_get_remaining_time(uint32_t sample, uint32_t* time)
{
  double a_time=0;

  if (!time || !pa_stream)
    {
      SHOW("event get_remaining_time> %s\n","audio device not available");	  
      return -1;
    }

  if (sample > myReadPosition)
    {
      // TBD: take in account time suplied by portaudio V18 API
      a_time = sample - myReadPosition;
      a_time = 0.5 + (a_time * 1000.0) / wave_samplerate;
    }
  else
    {
      a_time = 0;
    }

  SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time);

  *time = (uint32_t)a_time;

  return 0;
}

//>
//<wave_test_get_write_buffer

void *wave_test_get_write_buffer()
{
  return myWrite;
}


#else
// notdef USE_PORTAUDIO


void wave_init(int srate) {}
void* wave_open(const char* the_api) {return (void *)1;}
size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize) {return theSize;}
int wave_close(void* theHandler) {return 0;}
int wave_is_busy(void* theHandler) {return 0;}
void wave_terminate() {}
uint32_t wave_get_read_position(void* theHandler) {return 0;}
uint32_t wave_get_write_position(void* theHandler) {return 0;}
void wave_flush(void* theHandler) {}
typedef int (t_wave_callback)(void);
void wave_set_callback_is_output_enabled(t_wave_callback* cb) {}
extern void* wave_test_get_write_buffer() {return NULL;}

int wave_get_remaining_time(uint32_t sample, uint32_t* time)
{
	if (!time) return(-1);
	*time = (uint32_t)0;
	return 0;
}

#endif  // of USE_PORTAUDIO

//>
//<clock_gettime2, add_time_in_ms

void clock_gettime2(struct timespec *ts)
{
  struct timeval tv;

  if (!ts)
    {
      return;
    }

  assert (gettimeofday(&tv, NULL) != -1);
  ts->tv_sec = tv.tv_sec;
  ts->tv_nsec = tv.tv_usec*1000;
}

void add_time_in_ms(struct timespec *ts, int time_in_ms)
{
  if (!ts)
    {
      return;
    }

  uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms;
  while(t_ns >= ONE_BILLION)
    {
      SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns);
      ts->tv_sec += 1;
      t_ns -= ONE_BILLION;	  
    }
  ts->tv_nsec = (long int)t_ns;
}


#endif   // USE_ASYNC

//>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
CEO bring-it-together s.r.o.
Slovakia Slovakia
Jozef Božek is currently a software engineer at bring-it-together s.r.o. in area of large scale infomation systems and mobile applications development.
He has been developing in C++ nearly full time since 2000, in Java since 2004 and in Objective-C since 2009. He is programming using Java EE SDK, iOS SDK, COM/DCOM, MFC, ATL, STL and so on Smile | :)

Comments and Discussions