Click here to Skip to main content
15,891,777 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 74.7K   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>                  *
 *   eSpeak driver for PulseAudio                                          *
 *   based on the XMMS PulseAudio Plugin                                   *
 *                                                                         *
 *   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.             *
 ***************************************************************************/
// TBD:
// * ARCH_BIG
// * uint64? a_timing_info.read_index
// * prebuf,... size?
// * 0.9.6: pb pulse_free using tlength=8820 (max size never returned -> tlength=10000 ok, but higher drain).
//
#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 <pulse/pulseaudio.h>
#include <pthread.h>

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

//<Definitions

enum {ONE_BILLION=1000000000};

enum {
//   /* 100ms. 
//      If a greater value is set (several seconds), 
//      please update _pulse_timeout_start accordingly */
//   PULSE_TIMEOUT_IN_USEC = 100000,  

  /* return value */
  PULSE_OK = 0,
  PULSE_ERROR = -1,
  PULSE_NO_CONNECTION = -2
};

#ifdef USE_PULSEAUDIO

static t_wave_callback* my_callback_is_output_enabled=NULL;

#define ESPEAK_FORMAT PA_SAMPLE_S16LE
#define ESPEAK_CHANNEL 1

#define MAXLENGTH 132300
#define TLENGTH 4410
#define PREBUF 2200
#define MINREQ 880
#define FRAGSIZE 0

static pthread_mutex_t pulse_mutex;

static pa_context *context = NULL;
static pa_stream *stream = NULL;
static pa_threaded_mainloop *mainloop = NULL;

static pa_cvolume volume;
static int volume_valid = 0;

static int do_trigger = 0;
static uint64_t written = 0;
static int time_offset_msec = 0;
static int just_flushed = 0;

static int connected = 0;
static int wave_samplerate;

#define CHECK_DEAD_GOTO(label, warn) do { \
if (!mainloop || \
    !context || pa_context_get_state(context) != PA_CONTEXT_READY || \
    !stream || pa_stream_get_state(stream) != PA_STREAM_READY) { \
        if (warn) \
            SHOW("Connection died: %s\n", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \
        goto label; \
    }  \
} while(0);

#define CHECK_CONNECTED(retval) \
do { \
    if (!connected) return retval; \
} while (0);

#define CHECK_CONNECTED_NO_RETVAL(id)					\
  do {									\
    if (!connected){ SHOW("CHECK_CONNECTED_NO_RETVAL: !pulse_connected\n", ""); return;	} \
  } while (0);

//>


// static void display_timing_info(const pa_timing_info* the_time)
// {
//   const struct timeval *tv=&(the_time->timestamp);

//   SHOW_TIME("ti>");
//   SHOW("ti> timestamp=%03d.%03dms\n",(int)(tv->tv_sec%1000), (int)(tv->tv_usec/1000));
//   SHOW("ti> synchronized_clocks=%d\n",the_time->synchronized_clocks);
//   SHOW("ti> sink_usec=%ld\n",the_time->sink_usec);
//   SHOW("ti> source_usec=%ld\n",the_time->source_usec);
//   SHOW("ti> transport=%ld\n",the_time->transport_usec);
//   SHOW("ti> playing=%d\n",the_time->playing);
//   SHOW("ti> write_index_corrupt=%d\n",the_time->write_index_corrupt);
//   SHOW("ti> write_index=0x%lx\n",the_time->write_index);
//   SHOW("ti> read_index_corrupt=%d\n",the_time->read_index_corrupt);
//   SHOW("ti> read_index=0x%lx\n",the_time->read_index);
// }


static void info_cb(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
  ENTER(__FUNCTION__);
    assert(c);

    if (!i)
        return;

    volume = i->volume;
    volume_valid = 1;
}

static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) {
    pa_operation *o;
  ENTER(__FUNCTION__);
    
    assert(c);

    if (!stream ||
        index != pa_stream_get_index(stream) ||
        (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
         t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW)))
        return;

    if (!(o = pa_context_get_sink_input_info(c, index, info_cb, NULL))) {
        SHOW("pa_context_get_sink_input_info() failed: %s\n", pa_strerror(pa_context_errno(c)));
        return;
    }
    
    pa_operation_unref(o);
}

static void context_state_cb(pa_context *c, void *userdata) {
  ENTER(__FUNCTION__);
    assert(c);

    switch (pa_context_get_state(c)) {
        case PA_CONTEXT_READY:
        case PA_CONTEXT_TERMINATED:
        case PA_CONTEXT_FAILED:
            pa_threaded_mainloop_signal(mainloop, 0);
            break;

        case PA_CONTEXT_UNCONNECTED:
        case PA_CONTEXT_CONNECTING:
        case PA_CONTEXT_AUTHORIZING:
        case PA_CONTEXT_SETTING_NAME:
            break;
    }
}

static void stream_state_cb(pa_stream *s, void * userdata) {
  ENTER(__FUNCTION__);
    assert(s);

    switch (pa_stream_get_state(s)) {

        case PA_STREAM_READY:
        case PA_STREAM_FAILED:
        case PA_STREAM_TERMINATED:
            pa_threaded_mainloop_signal(mainloop, 0);
            break;

        case PA_STREAM_UNCONNECTED:
        case PA_STREAM_CREATING:
            break;
    }
}

static void stream_success_cb(pa_stream *s, int success, void *userdata) {
  ENTER(__FUNCTION__);
    assert(s);

    if (userdata)
        *(int*) userdata = success;
    
    pa_threaded_mainloop_signal(mainloop, 0);
}

static void context_success_cb(pa_context *c, int success, void *userdata) {
  ENTER(__FUNCTION__);
    assert(c);

    if (userdata)
        *(int*) userdata = success;
    
    pa_threaded_mainloop_signal(mainloop, 0);
}

static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {
  ENTER(__FUNCTION__);
    assert(s);

    pa_threaded_mainloop_signal(mainloop, 0);
}

static void stream_latency_update_cb(pa_stream *s, void *userdata) {
  //  ENTER(__FUNCTION__);
    assert(s);

    pa_threaded_mainloop_signal(mainloop, 0);
}

static int pulse_free(void) {
  ENTER(__FUNCTION__);
    size_t l = 0;
    pa_operation *o = NULL;

    CHECK_CONNECTED(0);

    SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop_lock");
    pa_threaded_mainloop_lock(mainloop);
    CHECK_DEAD_GOTO(fail, 1);

    if ((l = pa_stream_writable_size(stream)) == (size_t) -1) {
        SHOW("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context)));
        l = 0;
        goto fail;
    }

    SHOW("pulse_free: %s (ret=%d)\n", "pa_stream_writable_size", l);

    /* If this function is called twice with no pulse_write() call in
     * between this means we should trigger the playback */
    if (do_trigger) {
        int success = 0;

	SHOW("pulse_free: %s (call)\n", "pa_stream_trigger");
        if (!(o = pa_stream_trigger(stream, stream_success_cb, &success))) {
            SHOW("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
            goto fail;
        }
        
	SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop");
        while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
            CHECK_DEAD_GOTO(fail, 1);
            pa_threaded_mainloop_wait(mainloop);
        } 
	SHOW("pulse_free: %s (ret)\n", "pa_threaded_main_loop");
       
        if (!success)
            SHOW("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
    }
    
fail:
    SHOW("pulse_free: %s (call)\n", "pa_operation_unref");
    if (o)
        pa_operation_unref(o);
    
    SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop_unlock");
    pa_threaded_mainloop_unlock(mainloop);

    do_trigger = !!l;
    SHOW("pulse_free: %d (ret)\n", (int)l);
    return (int) l;
}

static int pulse_playing(const pa_timing_info *the_timing_info) {
  ENTER(__FUNCTION__);
    int r = 0;
    const pa_timing_info *i;

    assert(the_timing_info);

    CHECK_CONNECTED(0);
    
    pa_threaded_mainloop_lock(mainloop);

    for (;;) {
        CHECK_DEAD_GOTO(fail, 1);

        if ((i = pa_stream_get_timing_info(stream)))
	  {
            break;        
	  }
        if (pa_context_errno(context) != PA_ERR_NODATA) {
            SHOW("pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context)));
            goto fail;
        }

        pa_threaded_mainloop_wait(mainloop);
    }

    r = i->playing;
    memcpy((void*)the_timing_info, (void*)i, sizeof(pa_timing_info));

    //    display_timing_info(i);

fail:
    pa_threaded_mainloop_unlock(mainloop);

    return r;
}


// static void pulse_flush(int time) {
//   ENTER(__FUNCTION__);
//     pa_operation *o = NULL;
//     int success = 0;

//     CHECK_CONNECTED();

//     pa_threaded_mainloop_lock(mainloop);
//     CHECK_DEAD_GOTO(fail, 1);

//     if (!(o = pa_stream_flush(stream, stream_success_cb, &success))) {
//         SHOW("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context)));
//         goto fail;
//     }
    
//     while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
//         CHECK_DEAD_GOTO(fail, 1);
//         pa_threaded_mainloop_wait(mainloop);
//     }

//     if (!success)
//         SHOW("pa_stream_flush() failed: %s", pa_strerror(pa_context_errno(context)));
    
//     written = (uint64_t) (((double) time * pa_bytes_per_second(pa_stream_get_sample_spec(stream))) / 1000);
//     just_flushed = 1;
//     time_offset_msec = time;
    
// fail:
//     if (o)
//         pa_operation_unref(o);
    
//     pa_threaded_mainloop_unlock(mainloop);
// }


static void pulse_write(void* ptr, int length) {
  ENTER(__FUNCTION__);


  SHOW("pulse_write > length=%d\n", length);

    CHECK_CONNECTED();

    pa_threaded_mainloop_lock(mainloop);
    CHECK_DEAD_GOTO(fail, 1);

    if (pa_stream_write(stream, ptr, length, NULL, PA_SEEK_RELATIVE, (pa_seek_mode_t)0) < 0) {
        SHOW("pa_stream_write() failed: %s", pa_strerror(pa_context_errno(context)));
        goto fail;
    }
    
    do_trigger = 0;
    written += length;

fail:
    
    pa_threaded_mainloop_unlock(mainloop);
}

static int drain(void) {
    pa_operation *o = NULL;
    int success = 0;
    int ret = PULSE_ERROR;

    ENTER(__FUNCTION__);

    CHECK_CONNECTED(ret);

    pa_threaded_mainloop_lock(mainloop);
    CHECK_DEAD_GOTO(fail, 0);

    SHOW_TIME("pa_stream_drain (call)");
    if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) {
        SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
        goto fail;
    }
    
    SHOW_TIME("pa_threaded_mainloop_wait (call)");
    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
        CHECK_DEAD_GOTO(fail, 1);
        pa_threaded_mainloop_wait(mainloop);
    }
    SHOW_TIME("pa_threaded_mainloop_wait (ret)");

    if (!success) {
      SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
    } 
    else {
      ret = PULSE_OK;
    }
    
fail:
    SHOW_TIME("pa_operation_unref (call)");
    if (o)
        pa_operation_unref(o);
 
    pa_threaded_mainloop_unlock(mainloop);
    SHOW_TIME("drain (ret)");
    
    return ret;
}


static void pulse_close(void) {

  ENTER(__FUNCTION__);
    
  drain();

  connected = 0;

  if (mainloop)
    pa_threaded_mainloop_stop(mainloop);

    connected = 0;

  if (context) {
    SHOW_TIME("pa_context_disconnect (call)");
    pa_context_disconnect(context);
    pa_context_unref(context);
    context = NULL;
  }
  
  if (mainloop) {
  SHOW_TIME("pa_threaded_mainloop_free (call)");
    pa_threaded_mainloop_free(mainloop);
    mainloop = NULL;
  }
  SHOW_TIME("pulse_close (ret)");
  
}


static int pulse_open()
{
  ENTER(__FUNCTION__);
    pa_sample_spec ss;
    pa_operation *o = NULL;
    int success;
    int ret = PULSE_ERROR;

    assert(!mainloop);
    assert(!context);
    assert(!stream);
    assert(!connected);

    pthread_mutex_init( &pulse_mutex, (const pthread_mutexattr_t *)NULL);

    ss.format = ESPEAK_FORMAT;
    ss.rate = wave_samplerate;
    ss.channels = ESPEAK_CHANNEL;

    if (!pa_sample_spec_valid(&ss))
      return false;

/*     if (!volume_valid) { */
    pa_cvolume_reset(&volume, ss.channels);
    volume_valid = 1;
/*     } else if (volume.channels != ss.channels) */
/*         pa_cvolume_set(&volume, ss.channels, pa_cvolume_avg(&volume)); */

    SHOW_TIME("pa_threaded_mainloop_new (call)");
    if (!(mainloop = pa_threaded_mainloop_new())) {
      SHOW("Failed to allocate main loop\n","");
        goto fail;
    }

    pa_threaded_mainloop_lock(mainloop);

    SHOW_TIME("pa_context_new (call)");
    if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak"))) {
      SHOW("Failed to allocate context\n","");
      goto unlock_and_fail;
    }

    pa_context_set_state_callback(context, context_state_cb, NULL);
    pa_context_set_subscribe_callback(context, subscribe_cb, NULL);

    SHOW_TIME("pa_context_connect (call)");
    if (pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL) < 0) {
        SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
	ret = PULSE_NO_CONNECTION;
        goto unlock_and_fail;
    }

    SHOW_TIME("pa_threaded_mainloop_start (call)");
    if (pa_threaded_mainloop_start(mainloop) < 0) {
      SHOW("Failed to start main loop","");
        goto unlock_and_fail;
    }

    /* Wait until the context is ready */
    SHOW_TIME("pa_threaded_mainloop_wait");
    pa_threaded_mainloop_wait(mainloop);

    if (pa_context_get_state(context) != PA_CONTEXT_READY) {
        SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
	ret = PULSE_NO_CONNECTION;
 	if (mainloop)
 	  pa_threaded_mainloop_stop(mainloop);
        goto unlock_and_fail;
    }

    SHOW_TIME("pa_stream_new");
    if (!(stream = pa_stream_new(context, "unknown", &ss, NULL))) {
        SHOW("Failed to create stream: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    pa_stream_set_state_callback(stream, stream_state_cb, NULL);
    pa_stream_set_write_callback(stream, stream_request_cb, NULL);
    pa_stream_set_latency_update_callback(stream, stream_latency_update_cb, NULL);



    pa_buffer_attr a_attr;

    a_attr.maxlength = MAXLENGTH;
    a_attr.tlength = TLENGTH;
    a_attr.prebuf = PREBUF;
    a_attr.minreq = MINREQ;
    a_attr.fragsize = 0;

    SHOW_TIME("pa_connect_playback");
    if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), &volume, NULL) < 0) {
        SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    /* Wait until the stream is ready */
    SHOW_TIME("pa_threaded_mainloop_wait");
    pa_threaded_mainloop_wait(mainloop);

    if (pa_stream_get_state(stream) != PA_STREAM_READY) {
        SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    /* Now subscribe to events */
    SHOW_TIME("pa_context_subscribe");
    if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) {
        SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }
    
    success = 0;
    SHOW_TIME("pa_threaded_mainloop_wait");
    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
        CHECK_DEAD_GOTO(fail, 1);
        pa_threaded_mainloop_wait(mainloop);
    }

    if (!success) {
        SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }

    pa_operation_unref(o);

    /* Now request the initial stream info */
    if (!(o = pa_context_get_sink_input_info(context, pa_stream_get_index(stream), info_cb, NULL))) {
        SHOW("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context)));
        goto unlock_and_fail;
    }
    
    SHOW_TIME("pa_threaded_mainloop_wait 2");
    while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
        CHECK_DEAD_GOTO(fail, 1);
        pa_threaded_mainloop_wait(mainloop);
    }

/*     if (!volume_valid) { */
/*         SHOW("pa_context_get_sink_input_info() failed: %s", pa_strerror(pa_context_errno(context))); */
/*         goto unlock_and_fail; */
/*     } */

    do_trigger = 0;
    written = 0;
    time_offset_msec = 0;
    just_flushed = 0;
    connected = 1;
    //    volume_time_event = NULL;
    
    pa_threaded_mainloop_unlock(mainloop);
    SHOW_TIME("pulse_open (ret true)");
   
    //    return true;
    return PULSE_OK;


unlock_and_fail:

    if (o)
        pa_operation_unref(o);
    
    pa_threaded_mainloop_unlock(mainloop);
    
fail:

    //    pulse_close();

  if (ret == PULSE_NO_CONNECTION) {
    if (context) {
      SHOW_TIME("pa_context_disconnect (call)");
      pa_context_disconnect(context);
      pa_context_unref(context);
      context = NULL;
    }
  
    if (mainloop) {
      SHOW_TIME("pa_threaded_mainloop_free (call)");
      pa_threaded_mainloop_free(mainloop);
      mainloop = NULL;
    }  
  } 
  else {
    pulse_close();
  }

  SHOW_TIME("pulse_open (ret false)");
  
  return ret;

}

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_set_callback_is_output_enabled

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

//>
//<wave_init

void wave_init(int srate)
{
  ENTER("wave_init");

  stream = NULL;
	wave_samplerate = srate;

  pulse_open();
}

//>
//<wave_open

void* wave_open(const char* the_api)
{
  ENTER("wave_open");
  return((void*)1);
}

//>
//<wave_write

size_t wave_write(void* theHandler, char* theMono16BitsWaveBuffer, size_t theSize)
{
  ENTER("wave_write");
  size_t bytes_to_write = theSize;
  char* aBuffer=theMono16BitsWaveBuffer;

  assert(stream);

  size_t aTotalFreeMem=0;

  pthread_mutex_lock(&pulse_mutex);

  while (1) 
    {
      if (my_callback_is_output_enabled 
	  && (0==my_callback_is_output_enabled()))
	{
	  SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
	  theSize=0;
	  goto terminate;
	}

      aTotalFreeMem = pulse_free();
      if (aTotalFreeMem >= bytes_to_write)
	{
	  SHOW("wave_write > aTotalFreeMem(%d) >= bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write);
	  break;
	}
 
      // TBD: check if really helpful
      if (aTotalFreeMem >= MAXLENGTH*2)
 	{
 	  aTotalFreeMem = MAXLENGTH*2;
 	}
       
      SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write);

      // 500: threshold for avoiding too many calls to pulse_write
      if (aTotalFreeMem>500)
	{
	  pulse_write(aBuffer, aTotalFreeMem);
	  bytes_to_write -= aTotalFreeMem;
	  aBuffer += aTotalFreeMem;
	}

      usleep(10000);
    }

  pulse_write(aBuffer, bytes_to_write);

 terminate:
  pthread_mutex_unlock(&pulse_mutex);
  SHOW("wave_write: theSize=%d", theSize);
  SHOW_TIME("wave_write > LEAVE");
  return theSize;
}

//>
//<wave_close

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

  int a_status = pthread_mutex_lock(&pulse_mutex);
  if (a_status) {
    SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__);
    return PULSE_ERROR;
  }
  
  drain();

  pthread_mutex_unlock(&pulse_mutex);
  SHOW_TIME("wave_close (ret)");

  return PULSE_OK;
}

//>
//<wave_is_busy

int wave_is_busy(void* theHandler)
{
  SHOW_TIME("wave_is_busy");

  pa_timing_info a_timing_info;
  int active = pulse_playing(&a_timing_info);
  SHOW("wave_is_busy: %d\n",active);
  return active;
}

//>
//<wave_terminate

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

//   Pa_Terminate();

  int a_status;
  pthread_mutex_t* a_mutex = NULL;
  a_mutex = &pulse_mutex;
  a_status = pthread_mutex_lock(a_mutex);

  pulse_close();

  SHOW_TIME("unlock mutex");
  a_status = pthread_mutex_unlock(a_mutex);
  pthread_mutex_destroy(a_mutex);
}

//>
//<wave_get_read_position, wave_get_write_position, wave_get_remaining_time

uint32_t wave_get_read_position(void* theHandler)
{
  pa_timing_info a_timing_info;
  pulse_playing(&a_timing_info);
  SHOW("wave_get_read_position > %lx\n", a_timing_info.read_index);
  return a_timing_info.read_index;
}

uint32_t wave_get_write_position(void* theHandler)
{
  pa_timing_info a_timing_info;
  pulse_playing(&a_timing_info);
  SHOW("wave_get_read_position > %lx\n", a_timing_info.write_index);
  return a_timing_info.write_index;
}

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

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

  pa_timing_info a_timing_info;
  pulse_playing(&a_timing_info);

  if (sample > a_timing_info.read_index)
    {
      // TBD: take in account time suplied by portaudio V18 API
      a_time = sample - a_timing_info.read_index;
      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 NULL;
}


#else
// notdef USE_PULSEAUDIO


void wave_init() {}
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