diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..16c7f08 --- /dev/null +++ b/.clang-format @@ -0,0 +1,41 @@ +--- +Language: Cpp +BasedOnStyle: LLVM + +AlignAfterOpenBracket: Align +AlignEscapedNewlines: Left +AlignOperands: AlignAfterOperator +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: false +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Linux +ColumnLimit: 80 +IndentPPDirectives: BeforeHash +IndentWidth: 4 +InsertTrailingCommas: None +KeepEmptyLinesAtTheStartOfBlocks: false +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Before +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 5 +TabWidth: 4 +UseTab: AlignWithSpaces +... diff --git a/.gitignore b/.gitignore index 7870fbc..1b2211d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1 @@ -espeakup -*.d -*.o -*.swp +build* diff --git a/ChangeLog b/ChangeLog deleted file mode 100644 index b276f00..0000000 --- a/ChangeLog +++ /dev/null @@ -1,1806 +0,0 @@ -commit 2964310b2411c10756712ba902687e8388c78142 -Author: William Hubbs -Commit: William Hubbs - - makefile: add target to generate changelog - -commit 918e8853cdd8270c77f641c69871851effe18416 -Author: William Hubbs -Commit: William Hubbs - - version 0.80 - -commit 3e5815429bd702d9b0af68d793e6c83f035f9e23 -Author: William Hubbs -Commit: William Hubbs - - Add my email address to the copyright statement - -commit 8b49d9d211f917f7ee2009569f746659ec9096a8 -Author: Christopher Brannon -Commit: William Hubbs - - Fix implicit function declaration warning. - - This fixes #7 - -commit 97adab70de5e49dde3ac26774a636cbca48558c0 -Author: Christopher Brannon -Commit: William Hubbs - - Replace usage of daemon(3). - - Original patch and commit message courtesy of: - Samuel Thibault - - currently espeakup uses daemon() to do the daemonizing stuff. - Unfortunately, daemon() does things not very appropriately, and there - is notably a delay between the parent exit()ing and the child writing - the pid file. The attached patch reimplements it properly, espeakup - then notably plays much more nicely with systemd. - - Modified by Chris to apply to master. - This fixes #8. - -commit 92903254894d2b6f2c398a104e78d15553de6017 -Author: Christopher Brannon -Commit: William Hubbs - - Fix spelling keystrokes and char-by-char echo. - - Use ssml's interpret-as="characters" setting when the kernel reports - just one character. This allows the use of espeak's internationalized - spelling of letters instead of having to maintain spelling ourselves in speakup. - - Original patch courtesy of - Samuel Thibault - and modified to work with the current code by Chris. - - This fixes #6. - -commit ee099174d849e32bf7b555e458963d27f84c64b2 -Author: Samuel Thibault -Commit: William Hubbs - - Allow a voice to be selected by language name - - This allows the -V option on the command line to be a language name. - -commit c1ad891f2e321b052802a2c3c121522757948e61 -Author: Samuel Thibault -Commit: William Hubbs - - Create pid file when espeakup is really ready - - This makes sure that we do not report that we are ready until everything - is initialized. - -commit d97724373556e5ad6d632249bcc0ba4ef7aec4d8 -Author: Christopher Brannon -Commit: William Hubbs - - Add a missing #include, so that this can be built with musl. - - This closes #5. - -commit d95ee07775f6a63d80323e5ea242c530adf9c79b -Author: William Hubbs -Commit: William Hubbs - - Revert "add indexing support" - - This reverts commit e84e000b3ec9d393d720845a5f5fd05aa2ee7302. - I need to think more about how to implement this. - -commit e84e000b3ec9d393d720845a5f5fd05aa2ee7302 -Author: William Hubbs -Commit: William Hubbs - - add indexing support - -commit 3fbbdf19224c26b80b161666b37cec70ac5dd6d2 -Author: William Hubbs -Commit: William Hubbs - - Do not try to remove the pid file unless we are in speakup mode - -commit 58ed438f00c0f79c87885b73292c3b4e5c44e0d4 -Author: William Hubbs -Commit: William Hubbs - - rework two if statements - - These if statements were executing code if we were not in acsint mode. - They have been combined and the code is now executed when we are in - speakup mode, which is what we want. - -commit b2bd1d33a8a8d08ea9316bcf3cf8169545e7df78 -Author: William Hubbs -Commit: William Hubbs - - make espeakup's default rate closer to espeak's default - -commit 3b4b6d0cbc98d175f00a9f1e744406e3491c6d85 -Author: William Hubbs -Commit: William Hubbs - - change code to use allocMem wrapper for memory allocation - - One of the new string handling routines is a wrapper for allocating - memory. This commit changes the rest of the code to take advantage of - that wrapper. - -commit 6180ff6e49371d9fc1cfb5afb05941222d925d70 -Author: Christopher Brannon -Commit: Christopher Brannon - - Don't check to see if espeakup is running in acsint mode. - - This check is important when running with speakup, since there can only - be one instance accessing /dev/softsynth. - It is unnecessary in acsint mode. - -commit ba316a4cd4ad42b5cf7a550953c1dc26338e3569 -Author: William Hubbs -Commit: William Hubbs - - separate string handling routines into their own module - -commit c06f18c4544a6e9e3d28b8f037bc40588fbff3f5 -Author: Christopher Brannon -Commit: William Hubbs - - support adapters using the acsint module - -commit 70f74657c274d37f94cf7a4eabcd1d06bafbfcb5 -Author: William Hubbs -Commit: William Hubbs - - fix Makefile to use MANMODE to install man pages - -commit 999e6551b5999f5779299e51858e4bbb587f5561 -Author: William Hubbs -Commit: William Hubbs - - add pid path option to help - -commit 3353241a79f0e2b130dce5e98d656284a7da84f8 -Author: William Hubbs -Commit: William Hubbs - - add command line option to change the pid path - - This adds a -P or --pid-path option to the command line which - allows the user to change the path and the name of the pid file created - when espeakup is running as a daemon. - - I would like to thank Chris Brannon for the original idea for this. - -commit 49dcacb2eca6f808f1f52a4faaf96f1fae24e4b5 -Author: William Hubbs -Commit: William Hubbs - - adjust rate offset and multiplier for espeak 1.45.04 - -commit 2154d1a23157cc2b0ff72f1ed51e5c8a26b452cb -Author: William Hubbs -Commit: William Hubbs - - use memset to initialize sigaction structure - -commit 1990e8e25d23fd4cbaa924ac7af480e2a079b9dc -Author: Christopher Brannon -Commit: William Hubbs - - Properly initialize sigaction struct. - - The sigaction struct used in signal_thread was stored in an automatic - variable. The fields which were not set manually had undefined values. - -commit 701074fd9685ce4133672fbb35f046e76ab13422 -Author: William Hubbs -Commit: William Hubbs - - go back to just using a makefile - - The reason I went to autotools was the multiple sound systems, but since - we are now just using espeak's audio processing we can go back to a more - simple build system. - -commit 7bf2eee07a6c5d2120e62d13e0385e4c8e9c6782 -Author: William Hubbs -Commit: William Hubbs - - remove experimental alsa support - - The direct alsa support was experimental and never worked well. It had a - setting which was system specific. Also, I feel that it is better to let - espeak control the audio processing. - -commit c7ae47dfe59481b29ec80de2faaa6c8d3bd63379 -Author: William Hubbs -Commit: William Hubbs - - add experimental support for building a static binary - - This is done by adding a --enable-standalone switch to the configure - script. - -commit 3bfc662bae54038172de1c52b04e9c589d2f7bd0 -Author: William Hubbs -Commit: William Hubbs - - update location of latest version and git repository - -commit 037e6422179424dd40667765039c6584ac306514 -Author: William Hubbs -Commit: William Hubbs - - rename todo file - -commit 0d9d7b61419eb37247e8f7afb6c5b7ff198f0ab7 -Author: William Hubbs -Commit: William Hubbs - - update readme - -commit 056dcf70fe5a6850b73193ea06480bd955695e2e -Author: William Hubbs -Commit: William Hubbs - - convert to autotools - -commit d1630432ba55033c82da0dcae4a409f14da8d03f -Author: William Hubbs -Commit: William Hubbs - - re-organized the makefile. - -commit 8fda956e020abcde9365c20c6e14f3ebc15cd2e1 -Author: William Hubbs -Commit: William Hubbs - - fixed permissions in makefile - - It turns out that the install commands need to have the permission - options otherwise the permission of everything that is installed is 755, - which is not correct. - -commit 4cfcd7ba116ddd3e1c80744b4eb7e0b83438ec86 -Author: William Hubbs -Commit: William Hubbs - - fixed mandir - - the mandir variable in the makefile should point only to the top level - of the man tree. - -commit 1a10788c5f140f75e0f54a2baaf8625125bb52ac -Author: William Hubbs -Commit: William Hubbs - - lowered latency setting to 1/40 of a second. - -commit edb5e50fcd52d6f0ed9d2decb5c874dfd9395998 -Author: William Hubbs -Commit: William Hubbs - - make alsa code more readable - - This changes the code to use constants for the parameters to - snd_pcm_set_params. This makes it easier to read the code and to update - the values if needed. - -commit dec561324df67bd64eede09bcb2eb25273a04081 -Author: William Hubbs -Commit: William Hubbs - - updates to alsa support - - After studying pcm_min.c in the alsa library git repository, I updated - the alsa support to be similar to what I saw there. - -commit 486fe27f47d12a0482060aac4fc6c1568fab0613 -Author: William Hubbs -Commit: William Hubbs - - removed permission settings from makefile - -commit 311b6911959263a81f4dd74b44ad93af53a981b1 -Author: William Hubbs -Commit: William Hubbs - - fix makefile to not define variables if they are already defined - -commit ffe397fb911b0de8a14cacfc69bf9e683c9ef402 -Author: William Hubbs -Commit: William Hubbs - - removed the minimum function - - This function really wasn't needed. I also attempted to make the - callback functionn more like the test code in the alsa library git - repository. - -commit e66311bb992122ca4797eab1d2a79b1619c71b9b -Author: William Hubbs -Commit: William Hubbs - - added automatic dependency tracking to the Makefile - -commit 11cc053d822d563b9c040ce50341f8c8cd8b4f86 -Author: William Hubbs -Commit: William Hubbs - - reworked the makefile - - This version of the makefile should be more compatible with allowing - users to pass in cflags. - -commit 48fa03faf5529a4072ae50fe9ff983333bc6fca0 -Author: William Hubbs -Commit: William Hubbs - - renamed espeak_sound.c to portaudio.c - - This better describes the sound system that espeak uses natively. - -commit 654fc810fe981907d3f17d212ae51d9d6124fa42 -Author: William Hubbs -Commit: William Hubbs - - default prefix to /usr/local - - Without packaging, we should be installing espeakup in /usr/local. - -commit e4e3f0979e1820712a6a88bfcf60da34a8a747f0 -Author: William Hubbs -Commit: William Hubbs - - the status handle should be static - -commit 57547e8efa0dcf9f7f1750d7e8472f7207d20329 -Author: William Hubbs -Commit: William Hubbs - - renamed synth.c to espeak.c - - The name was changed because it describes the function of this code more - accurately. - -commit ac9e12414b2f4a1b38f8dabdaf7eb5ae33276008 -Author: William Hubbs -Commit: William Hubbs - - made stop_requested a global variable - - The two variables, stop_requested and runner_must_stop were performing - the same function, so I am using one variable, stop_requested for this - function. - -commit 18ebab3247934c71320abed559021fe6eebab530 -Author: William Hubbs -Commit: William Hubbs - - indentation fixes - -commit cc7e77eb9db904f27b9de12f4b25b17c2f6d5b02 -Author: William Hubbs -Commit: William Hubbs - - move stop_audio call to synth thread - - Since there is no reason currently for the softsynth thread to do this, - it makes better sense to have the synth thread control all interaction - with espeak. - -commit 32d848cb76de9853564d7e2ea37725c1f9af1ae4 -Author: William Hubbs -Commit: William Hubbs - - make callback honor should_run - - The callback should return and abort synthesis if should_run is 0. This - fixes slow shutdown times in alsa mode. - -commit 4de82bf24cff9e5a82a2645f30af0dbfae6ded05 -Author: William Hubbs -Commit: William Hubbs - - added a couple of #defines to the alsa code - -commit 739e074072100dd420b10eb1d8b8d55451b9bdea -Author: William Hubbs -Commit: William Hubbs - - reworked the queue_remove function - - Now, when queue_remove is called, it returns the pointer to the data of - the first entry in the queue and removes the entry. - -commit 014d27b7aa218db968ab19b2b493fe95406b4b8b -Author: William Hubbs -Commit: William Hubbs - - removed some unlock_audio_mutex() calls - -commit 82490a98d253d5201f93f309289346fff5169abc -Author: William Hubbs -Commit: William Hubbs - - call snd_pcm_prepare after snd_pcm_drop in stop_audio - -commit 101e14901e0517f6f09024dc8dc1f47dc682227a -Author: William Hubbs -Commit: William Hubbs - - removed white space in the makefile - -commit dc056e6c773e203af74d290393819b7fdc4043e0 -Author: William Hubbs -Commit: William Hubbs - - broke the queue definitions out into their own header file - -commit 0a9a8cce9bcb0310903aaffafd2dbdbe5a6458c4 -Author: William Hubbs -Commit: William Hubbs - - small style changes - -commit 40479540b4c8dd6a876b084bce0e6c806033da06 -Author: Christopher Brannon -Commit: William Hubbs - - Fix a memory leak. - - If we fail to add entries to the queue in queue_add_cmd or - queue_add_text, properly free the entry. - -commit ccb8cea91ee1537e24070dbd3cc568e6f24ddb28 -Author: William Hubbs -Commit: William Hubbs - - stop speech before clearing the queue - - Thanks to Kirk Reiser for pointing out that this makes the cancel - response faster. - -commit 31ad5f04ca691228889bd839852d8b19321c86c1 -Author: Christopher Brannon -Commit: William Hubbs - - Completely data-agnostic queue functions. - - The functions in queue.c no longer use static variables. We can now use - them for multiple queues, if necessary. - -commit 011ec271627615be44e1bacd6d269745c86028e9 -Author: Christopher Brannon -Commit: William Hubbs - - Create pipe before starting the signal handler thread. - - The thread can write to the pipe, so the pipe must be initialized - before the thread starts. - -commit 04d10d88ec0bb2161885bc3358a9bc6b5e06ce35 -Author: William Hubbs -Commit: William Hubbs - - more sound updates - - removed the user_data processing code and put the call to snd_pcm_drop - in stop_audio. - -commit ceaae3640a51fad408386f27bb30fe2063cdf58e -Author: William Hubbs -Commit: William Hubbs - - audio should be stopped in softsynth_thread not espeak_thread - -commit c9f871f687c6bd6ef1a562000c53c6762cfa7205 -Author: William Hubbs -Commit: William Hubbs - - see if we need to silence speech before we process the queue - -commit a82bbd81400cfa002835c35ce46a956cc81460ad -Author: William Hubbs -Commit: William Hubbs - - fixed a memory leak - - If we processed an entry from the queue successfully, we were removing - the entry itself from the queue but not freeing the memory allocated to - the entry. - -commit 58f09983f856703e8a06d9b16ef17b7d8728eb4d -Author: William Hubbs -Commit: William Hubbs - - fixed callback return code - - The callback should use the value of stop_requested as its return code. - -commit 0238baa5c29f9fef26b4608be24e4f63b2927e82 -Author: William Hubbs -Commit: William Hubbs - - more alsa updates - - Made an 'if' statement in the callback more clear and added some locking - for the audio mutex. - -commit bcd64cb263745e3994484156713a601db1b59990 -Author: William Hubbs -Commit: William Hubbs - - created a start_audio function - - This moves audio control to the specific files, espeak_sound.c and - alsa.c, which are tied to the sound systems. - -commit 9b7dcebb6d3dd84d19a448e7e79a1746cd939a62 -Author: William Hubbs -Commit: William Hubbs - - alsa updates - - The first while loop in the callback doesn't need to be a loop. If the - audio fails, we can just print an error and return. - -commit 46bf3d99d51960c7a179d9bfd6724c4a428e0658 -Author: William Hubbs -Commit: William Hubbs - - all access of the audio mutex should go through our functions - -commit 87ab6c6ea822f9f7b393d1cb83c9f579d728326a -Author: William Hubbs -Commit: William Hubbs - - make sure that snd_pcm_drop is successful. - - This was suggested by Kirk Reiser and Chris Brannon. - -commit 4889572ad18a8be877916d131d9a101b6563773c -Author: William Hubbs -Commit: William Hubbs - - use user_data to detect old events - - When espeak_Cancel is called, change the value of user_data that is - passed to the events, and, in the callback, use this to test to see if - cancel was received. If the value of user_data has changed, discarde - events that have the old value. This patch is from Chris Brannon. - -commit a9398bdeb413f8c9b88178756e0cdc7ede55b0bc -Author: William Hubbs -Commit: William Hubbs - - Added another error check for alsa - -commit be879d206b52221546e4b8e00b60e0c0cf70f855 -Author: William Hubbs -Commit: William Hubbs - - alsa update - - I changed the name of the callback to alsa_callback and removed a line - that was making the amount of data written to the sound card very small. - -commit c1d33d673838faf8b70cc42c72c119f5c0002d44 -Author: William Hubbs -Commit: William Hubbs - - Set the espeak audio buffer size to 50 ms - - This should help make the cancel command more responsive. - -commit 0471ff47f4dc031cf70b14eb15128fd60fb185dd -Author: William Hubbs -Commit: William Hubbs - - add support for the user_data parameter to espeak_synth - - The user_data parameter is just a pointer that is passed into the - espeak_synth call that is passed back to the callback. In native mode, - we are not using it since there is not a callback. However, in alsa - mode, it will be used to indicate when a cancel was processed. - -commit 520bbae36512731bc075870c0baa9339929b4fde -Author: William Hubbs -Commit: William Hubbs - - renamed stopped to stop_requested - - This is more descriptive of what the variable actually does. It signals - the callback to stop the audio. - -commit 8f3e8f196711d729de6c2dc1ed1ef2d086f99955 -Author: William Hubbs -Commit: William Hubbs - - moved the audio_mutex code to alsa - - This is not needed for native sound support, so it has been moved into - the alsa specific code. - -commit e49acbb59acdbad827da2fc4dd7061e7fe2de35a -Author: William Hubbs -Commit: William Hubbs - - removed a debug print - -commit 24bcdf5666d2546b9177b54b25d2fbfc54ab804b -Author: William Hubbs -Commit: William Hubbs - - another termination fix - - espeak_thread needs to signal softsynth_thread once more as it is going - town so that softsynth_thread will see that should_run is now 0 and - terminate. - -commit 5ec3809c599981f79db87f41f4b181eb5456a4f7 -Author: William Hubbs -Commit: William Hubbs - - wake up espeak_thread when softsynth_thread terminates - - espeak_thread needs a signal since it might be sleeping and - should_run has changed. This makes sure it terminates. - -commit 5ebe506026cf2a5953ba9773d2e2c434e8bbc6f4 -Author: William Hubbs -Commit: William Hubbs - - more mutex fixes - - Make sure that should_run is protected by the mutex in the softsynth - thread. - -commit d5fd5d69b84cdcde67357a5a934b343e46a32cb2 -Author: Chris Brannon -Commit: William Hubbs - - don't wait on a condition variable if should_run is false - -commit 633743117c7067bb7f7a7a12f2edbe6bb463a366 -Author: William Hubbs -Commit: William Hubbs - - mutex fixes - - We need to make sure that should_run is protected by the mutex. - -commit 1750b92cfb0e5d5a39564ea8d949113c4e0c3409 -Author: William Hubbs -Commit: William Hubbs - - fixed signal handling issue - - The signal handler stopped working after I moved the initialization - calls to the main function. Creating the signal handler thread first - fixed this issue. - -commit eb74a3ce171515dd1f2970dfb3d1b6c2f07c6d79 -Author: William Hubbs -Commit: William Hubbs - - removed an unnecessary call to espeak_Terminate() - -commit dd00775695f713667b2fc295feab6a2a2fe1926e -Author: William Hubbs -Commit: William Hubbs - - initialization update - - The main function now initializes espeak and opens the softsynth before - starting the threads. This insures that the resources we need are - active. - -commit 6a15f2cccf4d98e112f09ae64e68773c028b76f6 -Author: William Hubbs -Commit: William Hubbs - - fixed first wait in softsynth thread - - The thread should wait if there is nothing in the queue and if there is - not a request to stop. - Thanks to Chris Brannon for the patch. - -commit 3687f16b09d7ff37f73ca0f98acd8dedd4ce18c1 -Author: William Hubbs -Commit: William Hubbs - - wait for acknowledgements correctly - - pthread_cond_wait() can have spurious wakeups, so we need to be sure - that the condition is actually true when we return from this function. - Thanks to Chris Brannon for the patch. - -commit 938e10b66a14237cdff734ae5b97525f9c4ae9cc -Author: William Hubbs -Commit: William Hubbs - - removed a nested lock/unlock - -commit 975765289b3348219d0db8b2d64e2f4f7747f9f2 -Author: William Hubbs -Commit: William Hubbs - - made sure all cond_wait and cond_signal calls are inside lock/unlock - calls - -commit b8c7247feb05a07bab653cab2a6be50a4a17fd5d -Author: William Hubbs -Commit: William Hubbs - - removed acknowledge_guard and substituted queue_guard - -commit 1091182f884cb8187ce3cdaf3723243deaa4dcb2 -Author: William Hubbs -Commit: William Hubbs - - removed a debug print call - -commit 0bf2cae5a6cd35ea7ee78fbb136ccb58f6937b3e -Author: William Hubbs -Commit: William Hubbs - - moved lock/unlock in queue_process_entry - - The only time queue_process_entry should lock the queue gard is when it - is removing the item from the queue. This happens only when the item - was successfully processed. - -commit 554a03d26c147958b99b78690214e195cda65c41 -Author: William Hubbs -Commit: William Hubbs - - white space fix - -commit b7f324072f26fa31a8d9772f38bdf1f920504223 -Author: Christopher Brannon -Commit: William Hubbs - - Fix concurrency bugs. - - 1. Don't lock or unlock queue_guard during queue_clear. - It is locked when queue_clear is called, and it should remain so. - 2. Protect runner_must_stop with queue_guard in - the request_espeak_stop function. - The following condition should always hold: queue_guard is locked while - testing or modifying runner_must_stop. - 3. Rename stop_guard to acknowledge_guard. This is a more - descriptive name. This mutex simply protects the acknowledgement of - the stop request from being lost. - 4. Remove the pthread_mutex_lock from the top of queue_process_entry, - because queue_guard is already locked when the function is called. - -commit a8cad20de898002465fad6f3cf42f381f2a33812 -Author: William Hubbs -Commit: William Hubbs - - more multithreading work - - Rearranged the queue handling code so that queue.c is generic. Also - rearranged several functions in the threads. - -commit 42c3f76a083890c49fcb7d92a2f695f69e4882b4 -Author: William Hubbs -Commit: William Hubbs - - moved include for pthread.h to espeakup.h - -commit 3e8e7d12ae89b4b2c00596cf88fcb35fd4b5b20e -Author: William Hubbs -Commit: William Hubbs - - added back the declaration for softFD - -commit af5717b8cb3d9c2232f20f90d32601c0f7021fca -Author: William Hubbs -Commit: William Hubbs - - moved queue_add_xxx functions to softsynth thread - -commit c6b57885c9479f4d85831b2082206bef685cd74d -Author: William Hubbs -Commit: William Hubbs - - removed declaration of rate from main - -commit d82bfcd09ccd4145d17473bc9ae60af223a5f66e -Author: Christopher Brannon -Commit: William Hubbs - - Make one thread responsible for handling espeak interaction. - - Most of the idea for this change came from William: - Renamed queue_runner to espeak_thread. Moved espeak initialization - and termination to espeak_thread. The while loops that process - the queue now use the variable should_run. - -commit 0fdca827b851fd0beb9f476c52ec5568ed36174e -Author: William Hubbs -Commit: William Hubbs - - check for terminalFD after select() - - If terminalFD has something to read, we break out of the loop in the - softsynth thread. - -commit 474580b08b32d713ffb6f23c486685f1927e12c0 -Author: William Hubbs -Commit: William Hubbs - - add pipe to wake up the softsynth thread - - This adds a pipe to wake up the softsynth thread, in case we receive a - signal while it is in a select. Thanks to Chris Brannon. - -commit c5fce64f259bce473cd3c9371d7b095639a87fe5 -Author: William Hubbs -Commit: William Hubbs - - removed open_softsynth and close_softsynth - - The thread can now handle the softsynth device, so main doesn't need to - call these functions. - -commit a58f93cd74fead43927093d36fc775a976788a4b -Author: William Hubbs -Commit: William Hubbs - - renamed reader_thread to softsynth_thread - -commit dfc9bbff6518b2de6e4464fd348ae460766606fb -Author: William Hubbs -Commit: William Hubbs - - fixed should_run declaration - - Removed the local declaration of should_run and set up the extern. - -commit 9d1cabdd0d513c45bafdf7cc9c62b503f4a0f31d -Author: William Hubbs -Commit: William Hubbs - - started work on multi-threading more of the program - - The goal is to create threads for the reader, que runner/espeak - processing and signal handling. - As of this commit, this code is still being worked on, so it is broken. - -commit f58d9984ce6d615158d53a6cf1b5d503296973ab -Author: William Hubbs -Commit: William Hubbs - - Now the queue runner/softsynth handler clears the queue - - Thanks to Chris Brannon for the patch. - -commit 24e7d667bce7568585e1e644b1935c8fe0709b75 -Author: William Hubbs -Commit: William Hubbs - - queue fixes - - This adds retry processing back to the queue functions. queue_remove - should only be called after the head entry on the queue is processed - successfully. - -commit 26ab109bd8e14fd3d26a434cac2e5760b23dbf0a -Author: William Hubbs -Commit: William Hubbs - - moved the check for stop out of the loop - - In the callback, we should check to see if the stopped flag is true - whether or not we are processing audio. - -commit b108764b02e0d6eb1e8a3074cf2f85f10eb01d03 -Author: William Hubbs -Commit: William Hubbs - - removed the audio_callback variable - -commit 739d79cff89f2792aa47813e08bdcbb3065c32de -Author: Christopher Brannon -Commit: William Hubbs - - Select audio mode before initializing espeak. - -commit ec9d8b1ee23095afadd07bd05cbf1ca21df11b8f -Author: Christopher Brannon -Commit: William Hubbs - - Add error-checking to the snd_pcm_set_* calls. - - These can fail. They do more than simply manipulate a structure. - -commit a5d1a48f42bbb3de67551113c1f15da10f7b916d -Author: Christopher Brannon -Commit: William Hubbs - - Obtain sample rate from the value of espeak_Initialize. - - espeak uses a sample rate of 22050 HZ, but let's not rely on that knowledge. - espeak_Initialize returns the sample rate on success, - so rely on that value when selecting a rate. - -commit 3bb78df86c3a135660864e1e07c1d5266a575bce -Author: William Hubbs -Commit: William Hubbs - - Do not set the period. - -commit 94e23a3a027dfd887f6713e745a0b97ec09dfbc7 -Author: William Hubbs -Commit: William Hubbs - - fixed error condition check in alsa.c - - The check was looking for a specific error when it should have been just - checking for failure. - -commit a69342fcada7fe6533b81c1ae0bad23f983a399a -Author: William Hubbs -Commit: William Hubbs - - removed some blank lines and put the variables at the top of the file - -commit 91b8960b6add66a982327e68d51f75d25f828af8 -Author: Christopher Brannon -Commit: William Hubbs - - Protect the stopped variable with a mutex. - - An oversight. Should have done this in the initial commit. - volatile does not imply atomic. - -commit 0ad70b4eaa35d57867f20c631f60841fde824e20 -Author: William Hubbs -Commit: William Hubbs - - Revert "fixed stop_speech issue" - - This reverts commit 1c440e5a42ef2606c0330f816b00172ab309505c. - -commit 1d02169aa584ebb8c8739c4b1255abd3ee51e709 -Author: William Hubbs -Commit: William Hubbs - - alsa support is conditional - - This commit updates the makefile and the documentation to explain how to - build alsa support. It has not been fully tested, so it is not built by - default. Also, I was able to remove the conditional compile directives - from the source. - -commit 1c440e5a42ef2606c0330f816b00172ab309505c -Author: William Hubbs -Commit: William Hubbs - - fixed stop_speech issue - - The stop_speech function needs to test the return code from - espeak_Cancel() to be sure the operation was successful before - signaling the callback to stop the audio. - -commit 08e46c58a804c380f73a8be3403663af790a48bd -Author: William Hubbs -Commit: William Hubbs - - indentation fixes - -commit d373fb2aa7fbf673594cd98225877c6998dc99ad -Author: Christopher Brannon -Commit: William Hubbs - - An initial stab at ALSA support. - - It's very raw right now. - -commit b31985f97c30b043dba7b77c5af0b42d3a65fcdd -Author: William Hubbs -Commit: William Hubbs - - released v0.71 - -commit d7dd0f919dc82cc0e497700412980ce771a5d7df -Author: William Hubbs -Commit: William Hubbs - - fixed initialization issues - - We were not returning exit codes properly if we were unable to open the - softsynth or if the daemon was already running. - -commit 2db53856a90a82c99c759399a1dfa5e704ce4f4a -Author: William Hubbs -Commit: William Hubbs - - fixed typo in tarball script - -commit 55f8ebf98a0fd2caef675e22499798b8ead99b12 -Author: William Hubbs -Commit: William Hubbs - - released v0.70 - -commit 69f1e8554a65b3eb0bdfcaf56bbcea86acc7e926 -Author: William Hubbs -Commit: William Hubbs - - The tarball script now adds a ChangeLog - -commit 0e47c95015a8d0ca3c777717f3187bafc615f201 -Author: William Hubbs -Commit: William Hubbs - - updated README - -commit c235a3b063a524cd308f2512c14502b5dee71434 -Author: William Hubbs -Commit: William Hubbs - - added .indent.pro to the repository - -commit d7c81f5117442d66358efb52232d51f45a90d4e8 -Author: William Hubbs -Commit: William Hubbs - - indentation fixes - -commit 3ddbb94e37a5183a0010a2e1ae46f4a64d39bba4 -Author: Christopher Brannon -Commit: William Hubbs - - multithreading - - Make espeakup a multi-threaded program. One thread reads from the softsynth - device, queuing text and synthesis commands. The other thread processes - items from the queue. - -commit 3a8323f98c702443f65b35bbfa84d3ae2c4c9b39 -Author: William Hubbs -Commit: William Hubbs - - Fixed typo in README - -commit e7d300a183668d24cc1250083bd09ab1abcc4cfb -Author: William Hubbs -Commit: William Hubbs - - turn off espeak's default processing of uppercase letters - - This needs to be turned off since speakup processes upper case by - raising the pitch. - -commit a1510b5e93de11dbf70fec2295b30a87656b4824 -Author: William Hubbs -Commit: William Hubbs - - indentation fixes - -commit 3dbdcb21cbb28dc6cebd80077a6e774cc2673bb6 -Author: William Hubbs -Commit: William Hubbs - - Aespeakup should not drop all non-ascii characters. - - This fixes an issue with non-english languages. - Thanks to Samuel Thibault for the patch. - -commit 9551ba81d9bffad667f0d5f864d07c767e2e70e6 -Author: William Hubbs -Commit: William Hubbs - - espeakup 0.60 - -commit 6366b41bdf7b28b392de8ca0c2a246a8360f9293 -Author: William Hubbs -Commit: William Hubbs - - espeakup v0.6 - -commit 40f152e11fb4a89546e459861ac0348c58c8d449 -Author: William Hubbs -Commit: William Hubbs - - allow users to override CFLAGS - - This fixes an issue with the Makefile that was not allowing users to - override cflags and keeping -Wall in the flags when compiling. - -commit 4e7ae23757290ef299a3ba4285bc031de425d788 -Author: William Hubbs -Commit: William Hubbs - - created tarball script - - This commit adds a script to create a tarball from the repository and - removes this functionality from the makefile. - -commit 9ee3fd433cb2513462327fd815f108dc9abc8e8e -Author: William Hubbs -Commit: William Hubbs - - documented --default-voice in man page - - This commit adds the documentation for --default-voice to the espeakup - man page. - -commit e2493db48e69307eeee3390d9329212cb6403613 -Author: William Hubbs -Commit: William Hubbs - - add --default-voice option to the help and README - - This commit adds the documentation to the help and README files for the - --default-voice command line option. - -commit 2351b5d489454c32c4a5c2ccdb1da609ded03b70 -Author: William Hubbs -Commit: William Hubbs - - add support for setting the default voice - - This adds support for a --default-voice or -V (upper case) command line - option which will set the default voice espeakup uses. This takes a - name of an espeak voice -- for example: - - espeakup --default-voice=en-us - - or - - espeakup -V en-us - -commit da15136aa7b8c12024d9178d1ba6b29037984a75 -Author: William Hubbs -Commit: William Hubbs - - only one espeakup daemon should be running - - This fixes a bug which would allow espeakup in debug mode to be run even - if espeakup was already running as a daemon. - -commit 8dca597f29e0ef54525f5f717a7058a037d2e9fc -Author: William Hubbs -Commit: William Hubbs - - Added a version script - - I added a version script. This is used in the Makefile to get the - version of espeakup when none is specified when a tarball is created. - -commit bbf77d918dac93e1bec7fc90c0b8b34dfb5a8e65 -Author: William Hubbs -Commit: William Hubbs - - Cleaned up warnings and adjusted CFLAGS - - This commit cleans up warnings and adjusts CFLAGS. Thanks to - samuel.thibault@ens-lyon.org. - -commit 7db55303088588a19fcde1c804268c984f6008d8 -Author: William Hubbs -Commit: William Hubbs - - Espeakup version 0.51. - -commit 341fa7ba425892c12ae293584aacd9028d520e62 -Author: William Hubbs -Commit: William Hubbs - - fixed install command in makefile. - -commit 0f24afef95c2bdda474d77bc774f19003015da46 -Author: William Hubbs -Commit: William Hubbs - - espeakup v0.5 - -commit c12ec3e1b060b6e3ec1ac53db33b6f87877e9109 -Author: William Hubbs -Commit: William Hubbs - - moved version definition - - This commit moves the version definition to espeakup.c instead of cli.c - -commit a8876a79644f36453a7edaef4c0860c7d2402c9b -Author: William Hubbs -Commit: William Hubbs - - fixed the license in the man page - - The man page said that espeakup is under gpl version 2 or later, but it - is under version 3 or later, so I fixed the man page. - -commit 2b96dd2a026bb2220071699fc51cdf11b9412d82 -Author: William Hubbs -Commit: William Hubbs - - updated man page - - This commit re-words the description of espeakup in the man page. - -commit 17553c3c391e011a1ccf90179c634003d2a61519 -Author: William Hubbs -Commit: William Hubbs - - fixed the makefile - - This commit removes the definitions for CC, RM and INSTALL from the - makefile so that it will use system defaults for these commands. - -commit ace8acf7d01cc256c1df96d3b03e21806772edb5 -Author: William Hubbs -Commit: William Hubbs - - fixed hyphenation - - This commit turns off hypenation in the man page. - -commit 84c473b690633999f47478dbf00c766d1a31628b -Author: William Hubbs -Commit: William Hubbs - - added man page - - This commit adds a man page for espeakup. - Thanks to Chris Brannon for writing it. - -commit f7ddd8dc77927d21a07479f80969df031af396f2 -Author: William Hubbs -Commit: William Hubbs - - Released v0.4. - -commit a3901a7e4c0d273f2ad5352f939714fe235166e4 -Author: William Hubbs -Commit: William Hubbs - - fixed a bug in process_command - - One of the switch statements in process_command did not have a default - label, which lead to undefined behavior. - Thanks to Chris Brannon for the patch. - -commit 36b05aec419a7ab7bc3b66358f62122096ec9ce2 -Author: William Hubbs -Commit: William Hubbs - - Added support for the punctuation command from speakup - -commit c48b05d7437435c0c7513e9d98ab04b565c87f92 -Author: William Hubbs -Commit: William Hubbs - - moved all variable definitions to the top of the Makefile. - -commit 82fa63600b9377fa590a2c97ed737fd8c85a622c -Author: William Hubbs -Commit: William Hubbs - - fixed the makefile - - Added espeakup.h as a dependency in the makefile so that the sources - will be compiled if it changes. - -commit d7e8dc2b342b5a810ec561535ca0426969f519ea -Author: William Hubbs -Commit: William Hubbs - - Revert "fixed the length calculation when a flush is processed." - - This reverts commit 66cb5a7b5cfe0e569525935d03b0843ee85a8afc. - -commit 66cb5a7b5cfe0e569525935d03b0843ee85a8afc -Author: William Hubbs -Commit: William Hubbs - - fixed the length calculation when a flush is processed. - -commit 62deb008bc658d925bd86fb4172b12779b9cad18 -Author: William Hubbs -Commit: William Hubbs - - fixed the number of bytes to move in the memmove() call - -commit 14858818d3cf38886aef5089982522feb9a8edfb -Author: William Hubbs -Commit: William Hubbs - - changed strcpy to memmove - - The areas pointed to by strcpy() cannot overlap, so we need to use - memmove() in case that happens. - Thanks to Chris Brannon for pointing this out. - -commit 6bec55ca5789e26ee315e890f4937c1451b8cd07 -Author: William Hubbs -Commit: William Hubbs - - changed process_buffer to use the isprint() call. - -commit 1b608481a4062f3a8f98192e50acfcb73cf577e2 -Author: William Hubbs -Commit: William Hubbs - - the main loop now uses strrchr and strcpy - - The idea for this change came from another patch submitted by Chris - Brannon. We now use strrchr to look for the flush character and strcpy - to move the remainder of the buffer to the beginning. - -commit faab1893893fa0c97263a0edf965cf4f67bd1294 -Author: William Hubbs -Commit: William Hubbs - - Fixed the select call - - The select call was moved to be in the if statement below it since the - return code is not needed after that statement is processed. - -commit 24607b1cc8000d4d95128e96662240dbce385358 -Author: William Hubbs -Commit: William Hubbs - - made the synth flush character a constant for readability - -commit c8e2a35763624463d1006a83ab0125603813d239 -Author: William Hubbs -Commit: William Hubbs - - fixed an off-by-one error - - The loop can start at length-1 since we know that the last character in - the buffer is a null. - Thanks again to Chris Brannon for finding this. - -commit f76d00f6afefb8de92bf205d16d84f488b91615d -Author: William Hubbs -Commit: William Hubbs - - removed the callback function - - This commit removes the synthcallback function since it wasn't doing - anything. Also, it has been reported that this may be causing the - sluggishness when espeakup is asked to shut up. - Thanks to Chris Brannon for finding this. - -commit 17f07e8cd175520c8fe16880f9f578e0f49579fc -Author: William Hubbs -Commit: William Hubbs - - released v0.3. - -commit 9b0c6a8224980c8637302d9e680cccef4ed0043d -Author: William Hubbs -Commit: William Hubbs - - fixed the volume multiplier - - The volume range is actually 0-200 instead of 0-100, so the multiplier - needed to be adjusted. - -commit c7e3835fbd2ab4d9ae5162488163d22c8bd9c811 -Author: William Hubbs -Commit: William Hubbs - - Updated ToDo list - -commit 527fb136de9d01c752f6f6d03d2ac42b89a94499 -Author: William Hubbs -Commit: William Hubbs - - starting work toward supporting changing voices - - This commit adds a set_voice() function which will ultimately allow the - user to switch voices. - -commit f52c03ec2be8cc67e680eda0cdb6bbb051b858cd -Author: William Hubbs -Commit: William Hubbs - - Fixed a typo. - -commit 8d3552c9642aec00f5ecc89dbbccd181244d9bfa -Author: William Hubbs -Commit: William Hubbs - - updated readme - -commit 1f784be8911bd2917e697630de3da05da2814ab4 -Author: William Hubbs -Commit: William Hubbs - - added support for long command line options - -commit 037019de3a74c191349bebc03289a11ab7cead42 -Author: William Hubbs -Commit: William Hubbs - - Espeakup v0.2 - -commit 943ff9d3dc50184a359ee7b39d57b4baaba0657c -Author: William Hubbs -Commit: William Hubbs - - updated README - -commit 5ddeb1e978bc637b4db22f19d0db8e448599f5e0 -Author: William Hubbs -Commit: William Hubbs - - indentation fixes - -commit ccc9c4aa4ed3f16d3781b92fbd6d6ec013627c5c -Author: William Hubbs -Commit: William Hubbs - - modified rate offset - - This commit moves the rate offset to 84 instead of 80 so that the - highest espeak rate can be reached by setting the speakup rate to - the maximum. - -commit 96a3b198508172e30bb0614563db94f83e45ce03 -Author: William Hubbs -Commit: William Hubbs - - fix flush processing - - This removes flush processing from process_command() and fixes - the code in the main loop so that it does not ignore the 0 position - in the buffer. - -commit 5bc67dc49d61468968b25cda1340091fced573a0 -Author: William Hubbs -Commit: William Hubbs - - updated ToDo list. - -commit 02e72bda39b4c26bcc1a67d701a405427e3a6f8a -Author: William Hubbs -Commit: William Hubbs - - adjusted the rate multiplier - - This gets us closer to espeak's top speed, which is 390 wpm. - -commit ebcbcac9d2b230a37347f60aa3e2715a07bb13a6 -Author: William Hubbs -Commit: William Hubbs - - removed an extra assignment statement - -commit d2d763b36ba477c18a04933ee49b68f71dd641a2 -Author: William Hubbs -Commit: William Hubbs - - moved flush processing into main loop - - This patch from Kirk Reiser attempts to increase - responsiveness by moving the flush processing into the main loop. - -commit 564db7981e1aeda4eb84958d1f66c6474803991f -Author: William Hubbs -Commit: William Hubbs - - put back the select code - - This needed to be put back to keep espeakup from using 100% of the cpu. - -commit 5005b9ddde90aeab3948f9123491463d0e319528 -Author: William Hubbs -Commit: William Hubbs - - removed code that uses select - - I was using the wrong type for the return value for read(). - -commit 3e7e5be2b78bdc1dbaa400d3b8e35ac66eab3cbd -Author: William Hubbs -Commit: William Hubbs - - fixed bug with volume setting - - Now the volume setting should be announced again. - -commit f973c9ec3fa92560279acede8386beeaa5f6835b -Author: William Hubbs -Commit: William Hubbs - - cleaned up prototypes - - This commit gets rid of extra spaces in some function prototypes. - -commit 498ce65db7d789b9639b96240555e48ab3a910b6 -Author: William Hubbs -Commit: William Hubbs - - cleaned up process_command - -commit aac77a3cf1edd6b503bcaada06a575ac06efa6f7 -Author: William Hubbs -Commit: William Hubbs - - ignore object files - -commit de8d4d9dc3bb7afd51eef89c0c7c2fb6d630b7a7 -Author: William Hubbs -Commit: William Hubbs - - fixed an include for gcc 4.3 - -commit b34e397717bae998bd2280a0774a04cd37ea1791 -Author: William Hubbs -Commit: William Hubbs - - clear the queue when we catch a signal - -commit 4c30dac5e16c84d92f12aa06c3cf5279c726d8db -Author: William Hubbs -Commit: William Hubbs - - added prototypes for queue functions - -commit 2bf3b17ffcbb3705afc23957e77fba2dc7abbda9 -Author: William Hubbs -Commit: William Hubbs - - removed debug flag from cflags - -commit 655894ee2a240036e966a5854fc71bb788aebe25 -Author: William Hubbs -Commit: William Hubbs - - added queueing support - - I have added queueing support so that when we read from the softsynth we - can put the text and commands we have read into a queue. This will - allow us to retry calling espeak_synth() if we fill espeak's internal - buffer. - -commit 86e93edeb09774d3689cca7f9fe96aa484f3bd6d -Author: William Hubbs -Commit: William Hubbs - - removed softsynth code from main module - -commit 261c6db032a0c4654c0b77055701223bee055dcb -Author: William Hubbs -Commit: William Hubbs - - Added ToDo list - -commit 029dd03251d24dc5c0a1b0e5a16706c0ca73180d -Author: William Hubbs -Commit: William Hubbs - - modularize the code - - This commit just re-arranges the source so that it is easier to work with. - -commit 882e9c20a547707c2d63a70414de951c50e5d4db -Author: William Hubbs -Commit: William Hubbs - - clean up the main function - - This commit cleans up the main function and moves command line processing - to a separate function. - -commit 4d2f76d6e111bb45cdf336f4be8140bcab3d51b6 -Author: William Hubbs -Commit: William Hubbs - - removed makedist - - The functionality to create a tarball has been moved into the makefile. - Now it is possible to create a tarball by doing: - make tarball - creates a tarball of the llast tagged version. - make TAG=gittag tarball creates a tarball based on the tag that is given to it. - -commit 1802877912be8e4b68541ed07d738841947463fd -Author: William Hubbs -Commit: William Hubbs - - Removed init scripts. - - It will be best to let packagers write init scripts for their distributions, - so I am removing them from this repository. - -commit 3d83e0f6d152035890d7dbecb5961ee79234d476 -Author: William Hubbs -Commit: William Hubbs - - updated readme - - I added an acknowledgements section. - -commit b76fada0b61873bb5228246e2585fea3ca690aba -Author: William Hubbs -Commit: William Hubbs - - fixed makefile - - The reference to $(PROGRAM) should have been $<. - This is fixed. - -commit c2c1d429db31693a60b8e93479a4d4fe5d31588a -Author: William Hubbs -Commit: William Hubbs - - added redhat init script, thanks to William Acker. - -commit c7bece776d13a1c5ac1a52967ffb5cd1b3ed9a9c -Author: William Hubbs -Commit: William Hubbs - - fixed makefile - - There was a bug in the makefile which was not installing the binary correctly. - This is now fixed. - -commit 22ea5c4d1d5db2d4d24e5a89437fac0d6940edf7 -Author: William Hubbs -Commit: William Hubbs - - updated makedist - - The makedist script now excludes itself and .gitignore when creating a tarball. - -commit 9402716c32c124fff4614b1555f7219f0bbe7135 -Author: William Hubbs -Commit: William Hubbs - - cleaned up command line option processing - -commit 05b115abbb8aa1d14b0f1c38c1212918fc9d8c24 -Author: William Hubbs -Commit: William Hubbs - - fixed typos in makedist script. - -commit 16beed096923abea8a7768d1ee6224419faabd0b -Author: William Hubbs -Commit: William Hubbs - - added a script to make tarball releases. - -commit 6ecc3182dab397d7b197e18d7bf78ca8c1316f71 -Author: William Hubbs -Commit: William Hubbs - - added a version command line option and variable. - -commit 96871c66201b77fddf5b80106b8f136fe825df87 -Author: William Hubbs -Commit: William Hubbs - - added destdir and prefix to the makefile for packaging. - -commit c7e0e28ad1102c395958c3baab70b4a8d20203bc -Author: William Hubbs -Commit: William Hubbs - - added gpl - -commit 02c88e949a9e44e16fa7ad6c289f3c476e0becb2 -Author: William Hubbs -Commit: William Hubbs - - adjusted multipliers - - I adjusted the pitch and volume multipliers. - This should allow us to come closer to the maximum espeak settings. - -commit d7de943a3a1b748e2cb27f42af483339de8aa444 -Author: William Hubbs -Commit: William Hubbs - - added frequency support - - This patch, also from Kirk Reiser, adds frequency support. - -commit cddead8aa951e0ad68ba241456a8e5f36c6fb2f5 -Author: William Hubbs -Commit: William Hubbs - - pid file support - - This commit adds support for a pid file so that we can be stopped and - started by init systems. - -commit b51cf9d774cc5a2abd13480238b951e20680ec24 -Author: William Hubbs -Commit: William Hubbs - - Added a signal handler - - This commit adds a signal handler so that espeakup will terminate cleanly. - -commit 61aac5ecb58ac49fc9060d19dde2dbda996e8e9f -Author: William Hubbs -Commit: William Hubbs - - moved some variables - - This commit removes the softsynth file descriptor and the debug flag - from the synth structure. These will need to be accessed from a signal - handler and there is no way to pass the structure to it. - Also, the process_data function now works with local variables and assigns - them to the synth structure, avoiding allocating a buffer with malloc/free. - -commit eb5cb3d36d35887907e13aeee0cad8fcc1b73baa -Author: William Hubbs -Commit: William Hubbs - - fixed volume - - I applied another patch from Kirk Reiser which fixes the volume setting. - Thanks to Kirk for the patch. - -commit 7604cac68c53dd3c01cfc0e131e2a7f985b40ff7 -Author: William Hubbs -Commit: William Hubbs - - fixed the garbage characters bug - - This fixes the bug that was causing garbage characters to be spoken. Also - it sets the initial voice to "default". - -commit b745e0c48b614143fb4df04ba878af29f4ece587 -Author: William Hubbs -Commit: William Hubbs - - more error checking - - I moved the read call out of process_data and into the main loop. - Also, error checking was added for select() and read(). - -commit c3b0e8d0da9efdf9e719ba3fbe599640a5db7dce -Author: William Hubbs -Commit: William Hubbs - - code cleanup - - This patch adds the length of the buffer to the synth structure and - clears the buffer before we use it. - Thanks to Kirk Reiser for the patch. - -commit c4839ac78b967b4677531afe7568156c907e6bda -Author: William Hubbs -Commit: William Hubbs - - added maxBufferSize constant - -commit 754358f1731e58990b4c4131d4b900a5d2a5049a -Author: William Hubbs -Commit: William Hubbs - - added cli option support - - This commit adds support for command line options and also adds a debug option. - -commit 572f33337811973091f44e54e6b4fcb13ae689d9 -Author: William Hubbs -Commit: William Hubbs - - added volume support - - I added support for setting the volume. - -commit 4e3bbdb0b02eb1f12c087909e6437a31a9c60d7b -Author: William Hubbs -Commit: William Hubbs - - initial import diff --git a/Makefile b/Makefile deleted file mode 100644 index ad67b61..0000000 --- a/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -PREFIX = /usr/local -BINDIR = ${PREFIX}/bin -MANDIR = ${PREFIX}/share/man - -DEPFLAGS = -MMD -WARNFLAGS = -Wall -CFLAGS += ${DEPFLAGS} ${WARNFLAGS} - -LDLIBS = -lespeak -lpthread - -INSTALL = install -BINMODE = 0755 -MANMODE = 0644 -CHANGELOG_LIMIT?= --after="1 year ago" - -SRCS = cli.c \ - espeak.c \ - espeakup.c \ - queue.c \ - signal.c \ - softsynth.c \ - stringhandling.c - -OBJS = ${SRCS:.c=.o} - -all: espeakup - -changelog: - git log ${CHANGELOG_LIMIT} --format=full > ChangeLog - -install: espeakup - ${INSTALL} -d ${DESTDIR}${BINDIR} - ${INSTALL} -m ${BINMODE} $< ${DESTDIR}${BINDIR} - ${INSTALL} -d ${DESTDIR}${MANDIR}/man8 - ${INSTALL} -m ${MANMODE} espeakup.8 ${DESTDIR}${MANDIR}/man8 - -espeakup: ${OBJS} - -clean: - ${RM} *.d *.o - -distclean: clean - ${RM} espeakup - --include ${SRCS:.c=.d} diff --git a/README b/README deleted file mode 100644 index b8ed359..0000000 --- a/README +++ /dev/null @@ -1,70 +0,0 @@ -espeakup connector -======================= - -espeakup is a program which makes it possible for speakup to use -the espeak software synthesizer. It does this by reading speakup's -softsynth device and passing the text to espeak which actually speaks. - -Requirements -============ - -This program works with the speakup screen reader, which can be obtained -from http://linux-speakup.org, and the espeak software speech -synthesizer which can be obtained from http://espeak.sourceforge.net. -You must have both of these installed and operational. Setting them up -is beyond the scope of this document. - -Installation -============ - -The preferred way to install espeakup is using your distribution's -packaging system, but if your distribution does not have a package for -espeakup yet, espeakup just uses a Makefile, so you should be able to -change to the source directory, then type make, then as root, make -install. - -Starting Up -=========== - -This program should be run after speakup is set up to communicate with a -software synthesizer and after /dev/softsynth exists. The way this is -done is distribution specific, so it is beyond the scope of this -documentation. - -Command Line Options -==================== - -Espeakup currently accepts the following command line options: - - --default-voice=voice, -V voice Set default voice. - --debug, -d Debug mode (stay in the foreground). - --help, -h Show this help. - --version, -v Display the software version. - -Getting the Latest Version -========================== - -It is possible to download a tarball from github of any released version -as follows: - -wget http://www.github.com/williamh/espeakup/tarball/vx.y - -If you need a tarball for packaging purposes, one is available from -ftp://ftp.linux-speakup.org/pub/linux/goodies/espeakup-x.y.tar.bz2. - -The url for the git repository is git://github.com/williamh/espeakup.git. - -Acknowledgements -================ - -I would like to thank Marc Mulcahy, the author of the speakup to -TTSynth connector, on which this work is based. Also, I would like -to thank Kirk Reiser and Jonathan Duddington, the -authors of Speakup and Espeak, respectively, for their work. - -Questions -========= - -You can contact me with questions, bugs, patches, etc, at -w.d.hubbs@gmail.com or on the speakup mailing list. I hope you find -this software to be useful. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e1395b0 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# espeakup connector + +espeakup is a program which makes it possible for speakup to use +the espeak-ng software synthesizer. It does this by reading speakup's +softsynth device and passing the text to espeak-ng which actually speaks. + +## Requirements + +This program works with the speakup screen reader, which can be obtained +from http://linux-speakup.org, and the +[espeak-ng](https://github.com/espeak-ng/espeak-ng) software speech +synthesizer. + +You must have both of these installed and operational. Setting them up +is beyond the scope of this document. + +## Installation + +The preferred way to install espeakup is using your distribution's +packaging system, but if your distribution does not have a package for +espeakup yet, espeakup just uses meson, so you should be able to +change to the source directory, then type: + +```bash +meson setup . ./build +cd ./build +ninja +sudo ninja install +``` + +## Starting Up + +This program should be run after speakup is set up to communicate with a +software synthesizer and after /dev/softsynth exists. The way this is +done is distribution specific, so it is beyond the scope of this +documentation. + +## Acknowledgements + +I would like to thank Marc Mulcahy, the author of the speakup to +TTSynth connector, on which this work is based. Also, I would like +to thank Kirk Reiser and Jonathan Duddington, the +authors of Speakup and Espeak, respectively, for their work. + +## Filing Bugs + +Bugs should be filed on our bug tracker at +https://github.com/linux-speakup/issues. diff --git a/TODO b/TODO deleted file mode 100644 index ec1d210..0000000 --- a/TODO +++ /dev/null @@ -1,11 +0,0 @@ -This file contains a list of improvements that need to be made to -espeakup. - -Voice and language support should be added at some point. -This will consist of the following: -- add a function which will allow switching voices. (completed on 9/27) -- add a function/command which will allow speakup to find out which - voices espeak supports. -- define and add the command to speakup which will allow it to switch - voices. - diff --git a/doc/espeakup.8.ronn b/doc/espeakup.8.ronn new file mode 100644 index 0000000..598611c --- /dev/null +++ b/doc/espeakup.8.ronn @@ -0,0 +1,77 @@ + + +# espeakup(8) --- connect Speakup to the espeak-ng TTS engine + +## SYNOPSIS + +`espeakup` [`--pid-path=`] [`--alsa-volume`] +[`--default-voice=`[]] [`--debug`] [`--help`] [`--version`] + +## OPTIONS + + * `-P` , `--pid-path=`: + Set the full path for the pid file espeakup uses when in daemon mode. + + * `--alsa-volume`: + Drive the ALSA volume. useful for live environments where volume + adjustments maybe impossible. + + * `-V` , `--default-voice=`: + Set the espeak-ng voice to be used by default. + + * `-d`, `--debug`: + run in the foreground, rather than becoming a daemon process. + + * `-h`, `--help`: + display a brief help message and exit. + + * `-v`, `--version`: + output version information and exit. + +## DESCRIPTION + +espeakup bridges the gap between two tools: the Speakup screen review system and +the espeak-ng text-to-speech engine. Each of these tools performs a +well-defined task. + +Speakup is a kernel-based screen reader for the Linux console. It extracts and +processes the text that is displayed on the foreground virtual console. It +supports several hardware based speech synthesizers directly. However, since it +is in kernel space, it cannot support a software speech synthesizer directly +since these are in user space. + +espeak-ng is a popular software speech synthesizer. It is small, light weight, +very responsive, and supports multiple languages. espeakup is a connector which +will read text sent to it by speakup and forward it to espeak-ng. This allows +Speakup to use espeak-ng as its speech synthesizer. + +espeakup is a daemon. Typically, it is started at boot time, and it terminates +when the system is halted or rebooted. It should be started by the system's init +scripts. This process varies among Linux distributions, but the details are +usually managed by the person who packaged espeakup for your distribution. From +the perspective of an average user, espeakup's operation is invisible. + +## BUGS + +If you find a bug, please create a +[github issue](https://github.com/linux-speakup/espeakup/issues) +You might also consider mentioning it on the mailing list for the Speakup +screenreader. Visit +[list page](https://linux-speakup.org/cgi-bin/mailman/listinfo/speakup) +to learn more about the mailing list. + +## SEE ALSO + +For more information about Speakup, visit its +[homepage](https://linux-speakup.org). + +espeak-ng can be found at [github](https://github.com/espeak-ng/espeak-ng) + +## AUTHOR + +William Hubbs is the author of espeakup. + +This manual page was written by Chris Brannon . + +current authors and maintainers can be found at +[github](https://github.com/linux-speakup/espeakup/graphs/contributors) diff --git a/doc/meson.build b/doc/meson.build new file mode 100644 index 0000000..f055bb4 --- /dev/null +++ b/doc/meson.build @@ -0,0 +1,8 @@ +ronn = find_program('ronn', required: get_option('man')) +if ronn.found() + custom_target('man', + input : files('espeakup.8.ronn'), + output : 'espeakup.8', + command : [ronn, '--output-dir', '@OUTDIR@', '--roff', '@INPUT@'], + install : true, install_dir: join_paths(get_option('mandir'),'man8')) +endif diff --git a/espeak.c b/espeak.c deleted file mode 100644 index f09d3c4..0000000 --- a/espeak.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * espeakup - interface which allows speakup to use espeak - * - * Copyright (C) 2008 William Hubbs - * - * 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, see . - */ - -#define _GNU_SOURCE -#include -#include -#include -#include - -#include "espeakup.h" - -/* default voice settings */ -const int defaultFrequency = 5; -const int defaultPitch = 5; -const int defaultRate = 2; -const int defaultVolume = 5; -char *defaultVoice = NULL; - -/* multipliers and offsets */ -const int frequencyMultiplier = 11; -const int pitchMultiplier = 11; -const int rateMultiplier = 41; -const int rateOffset = 80; -const int volumeMultiplier = 22; - -volatile int stop_requested = 0; - -static int acsint_callback(short *wav, int numsamples, espeak_EVENT * events) -{ - int i; - for (i = 0; events[i].type != espeakEVENT_LIST_TERMINATED; i++) { - if (events[i].type == espeakEVENT_MARK) { - int mark = atoi(events[i].id.name); - if ((mark < 0) || (mark > 255)) - continue; - putchar(mark); - fflush(stdout); - } - } - return 0; -} - -static espeak_ERROR set_frequency(struct synth_t *s, int freq, - enum adjust_t adj) -{ - espeak_ERROR rc; - - if (adj == ADJ_DEC) - freq = -freq; - if (adj != ADJ_SET) - freq += s->frequency; - rc = espeak_SetParameter(espeakRANGE, freq * frequencyMultiplier, 0); - if (rc == EE_OK) - s->frequency = freq; - return rc; -} - -static espeak_ERROR set_pitch(struct synth_t *s, int pitch, - enum adjust_t adj) -{ - espeak_ERROR rc; - - if (adj == ADJ_DEC) - pitch = -pitch; - if (adj != ADJ_SET) - pitch += s->pitch; - rc = espeak_SetParameter(espeakPITCH, pitch * pitchMultiplier, 0); - if (rc == EE_OK) - s->pitch = pitch; - return rc; -} - -static espeak_ERROR set_punctuation(struct synth_t *s, int punct, - enum adjust_t adj) -{ - espeak_ERROR rc; - - if (adj == ADJ_DEC) - punct = -punct; - if (adj != ADJ_SET) - punct += s->punct; - rc = espeak_SetParameter(espeakPUNCTUATION, punct, 0); - if (rc == EE_OK) - s->punct = punct; - return rc; -} - -static espeak_ERROR set_rate(struct synth_t *s, int rate, - enum adjust_t adj) -{ - espeak_ERROR rc; - - if (adj == ADJ_DEC) - rate = -rate; - if (adj != ADJ_SET) - rate += s->rate; - rc = espeak_SetParameter(espeakRATE, - rate * rateMultiplier + rateOffset, 0); - if (rc == EE_OK) - s->rate = rate; - return rc; -} - -static espeak_ERROR set_voice(struct synth_t *s, char *voice) -{ - espeak_ERROR rc; - espeak_VOICE voice_select; - - rc = espeak_SetVoiceByName(voice); - if (rc != EE_OK) - { - memset(&voice_select, 0, sizeof(voice_select)); - voice_select.languages = voice; - rc = espeak_SetVoiceByProperties(&voice_select); - } - if (rc == EE_OK) - strcpy(s->voice, voice); - return rc; -} - -static espeak_ERROR set_volume(struct synth_t *s, int vol, - enum adjust_t adj) -{ - espeak_ERROR rc; - - if (adj == ADJ_DEC) - vol = -vol; - if (adj != ADJ_SET) - vol += s->volume; - rc = espeak_SetParameter(espeakVOLUME, (vol + 1) * volumeMultiplier, - 0); - if (rc == EE_OK) - s->volume = vol; - - return rc; -} - -static espeak_ERROR stop_speech(void) -{ - espeak_ERROR rc; - - rc = espeak_Cancel(); - return rc; -} - -static espeak_ERROR speak_text(struct synth_t *s) -{ - espeak_ERROR rc; - int synth_mode = 0; - - if (espeakup_mode == ESPEAKUP_MODE_ACSINT) - synth_mode |= espeakSSML; - - if (espeakup_mode == ESPEAKUP_MODE_SPEAKUP && (s->len == 1)) { - char *buf; - int n; - n = asprintf(&buf, - "%c", - s->buf[0]); - if (n == -1) { - /* D'oh. Not much to do on allocation failure. - * Perhaps espeak will happen to say the character */ - rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, - 0, synth_mode, NULL, NULL); - } else { - rc = espeak_Synth(buf, n + 1, 0, POS_CHARACTER, 0, - espeakSSML, NULL, NULL); - free(buf); - } - } else - rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, - synth_mode, NULL, NULL); - return rc; -} - -static void free_espeak_entry(struct espeak_entry_t *entry) -{ - assert(entry); - if (entry->cmd == CMD_SPEAK_TEXT) - free(entry->buf); - free(entry); -} - -static void synth_queue_clear() -{ - struct espeak_entry_t *current; - - while (queue_peek(synth_queue)) { - current = (struct espeak_entry_t *) queue_remove(synth_queue); - free_espeak_entry(current); - } -} - -static void queue_process_entry(struct synth_t *s) -{ - espeak_ERROR error; - static struct espeak_entry_t *current = NULL; - - if (current != queue_peek(synth_queue)) { - if (current) - free_espeak_entry(current); - current = (struct espeak_entry_t *) queue_remove(synth_queue); - } - pthread_mutex_unlock(&queue_guard); - switch (current->cmd) { - case CMD_SET_FREQUENCY: - error = set_frequency(s, current->value, current->adjust); - break; - case CMD_SET_PITCH: - error = set_pitch(s, current->value, current->adjust); - break; - case CMD_SET_PUNCTUATION: - error = set_punctuation(s, current->value, current->adjust); - break; - case CMD_SET_RATE: - error = set_rate(s, current->value, current->adjust); - break; - case CMD_SET_VOICE: - error = EE_OK; - break; - case CMD_SET_VOLUME: - error = set_volume(s, current->value, current->adjust); - break; - case CMD_SPEAK_TEXT: - s->buf = current->buf; - s->len = current->len; - error = speak_text(s); - break; - default: - break; - } - - if (error == EE_OK) { - free_espeak_entry(current); - current = NULL; - } -} - -int initialize_espeak(struct synth_t *s) -{ - int rate; - - /* initialize espeak */ - rate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 50, NULL, 0); - if (rate < 0) { - fprintf(stderr, "Unable to initialize espeak.\n"); - return -1; - } - - /* We need a callback in acsint mode, but not in speakup mode. */ - if (espeakup_mode == ESPEAKUP_MODE_ACSINT) - espeak_SetSynthCallback(acsint_callback); - - /* Setup initial voice parameters */ - if (defaultVoice) { - set_voice(s, defaultVoice); - free(defaultVoice); - defaultVoice = NULL; - } - set_frequency(s, defaultFrequency, ADJ_SET); - set_pitch(s, defaultPitch, ADJ_SET); - set_rate(s, defaultRate, ADJ_SET); - set_volume(s, defaultVolume, ADJ_SET); - espeak_SetParameter(espeakCAPITALS, 0, 0); - return 0; -} - -/* espeak_thread is the "main" function of our secondary (queue-processing) - * thread. - * First, lock queue_guard, because it needs to be locked when we call - * pthread_cond_wait on the runner_awake condition variable. - * Next, enter an infinite loop. - * The wait call also unlocks queue_guard, so that the other thread can - * manipulate the queue. - * When runner_awake is signaled, the pthread_cond_wait call re-locks - * queue_guard, and the "queue processor" thread has access to the queue. - * While there is an entry in the queue, call queue_process_entry. - * queue_process_entry unlocks queue_guard after removing an item from the - * queue, so that the main thread doesn't have to wait for us to finish - * processing the entry. So re-lock queue_guard after each call to - * queue_process_entry. - * - * The main thread can add items to the queue in exactly two situations: - * 1. We are waiting on runner_awake, or - * 2. We are processing an entry that has just been removed from the queue. -*/ -void *espeak_thread(void *arg) -{ - struct synth_t *s = (struct synth_t *) arg; - - pthread_mutex_lock(&queue_guard); - while (should_run) { - - while (should_run && !queue_peek(synth_queue) && !stop_requested) - pthread_cond_wait(&runner_awake, &queue_guard); - - if (stop_requested) { - stop_speech(); - synth_queue_clear(); - stop_requested = 0; - pthread_cond_signal(&stop_acknowledged); - } - - while (should_run && queue_peek(synth_queue) && !stop_requested) { - queue_process_entry(s); - pthread_mutex_lock(&queue_guard); - } - } - pthread_cond_signal(&stop_acknowledged); - pthread_mutex_unlock(&queue_guard); - return NULL; -} diff --git a/espeakup.8 b/espeakup.8 deleted file mode 100644 index 092c776..0000000 --- a/espeakup.8 +++ /dev/null @@ -1,78 +0,0 @@ -.\" Hey, Emacs! This is an -*- nroff -*- source file. -.\" Espeakup is Copyright 2008 by William Hubbs. -.\" This is free software; see the GNU General Public Licence version 3 -.\" or later for copying conditions. There is NO warranty. -.TH ESPEAKUP 8 "5 Nov 2008" "0.60" -.nh -.SH NAME -espeakup \(em connect Speakup to the ESpeak TTS engine -.SH SYNOPSIS -.B espeakup -[ -.B \-\^\-pid-path=path -] -[ -.B \-\^\-default-voice=voicename -] -[ -.B \-\^\-debug -] -[ -.B \-\^\-help -] -[ -.B \-\^\-version -] -.SH OPTIONS -.TP -.B \-P path, \-\^\-pid-path=path -Set the full path for the pid file espeakup uses when in daemon mode. -.TP -.B \-V voicename, \-\^\-default-voice=voicename -Set the espeak voice to be used by default. -.TP -.B \-d, \-\^\-debug -run in the foreground, rather than becoming a daemon process. -.TP -.B \-h, \-\^\-help -display a brief help message and exit. -.TP -.B \-v, \-\^\-version -output version information and exit. -.SH DESCRIPTION -Espeakup bridges the gap between two tools: the Speakup screen review -system and the ESppeak text-to-speech engine. Each of these tools -performs a well-defined task. Speakup is a kernel-based screen reader -for the Linux console. It extracts and processes the text that is -displayed on the foreground virtual console. It supports several -hardware based speech synthesizers directly. However, since it is in -kernel space, it cannot support a software speech synthesizer directly -since these are in user space. -ESpeak is a popular software speech synthesizer. It is small, light -weight, very responsive, and supports multiple languages. -Espeakup is a connector which will read text sent to it by speakup and -forward it to ESpeak. This allows Speakup to use ESpeak as its speech -synthesizer. -.PP -Espeakup is a daemon. Typically, it is started at boot time, and it terminates -when the system is halted or rebooted. It should be started by the -system's init scripts. This process varies among Linux distributions, -but the details are usually managed by the person who packaged Espeakup for -your distribution. -From the perspective of an average user, Espeakup's operation is invisible. -.SH BUGS -.PP -Espeakup is still classified as alpha software. Bugs are periodically found -and fixed. If you find a bug, please do report it to the author. You -might also consider mentioning it on the mailing list for the Speakup -screenreader. Visit http://speech.braille.uwo.ca/mailman/listinfo/speakup -to learn more about the mailing list. -.SH SEE ALSO -.PP -For more information about Speakup, visit its homepage: http://linux-speakup.org. -ESpeak's home page is http://espeak.sourceforge.net. -.SH AUTHOR -.PP -William Hubbs is the author and maintainer of Espeakup. He may be reached -via the email address . This manual page was written -by Chris Brannon, and his email address is . diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..38788d2 --- /dev/null +++ b/meson.build @@ -0,0 +1,25 @@ +project('espeakup', 'c', + default_options : [ + 'buildtype=debugoptimized', + 'c_std=gnu11', + 'warning_level=3' + ], + license : 'GPL-3.0-or-later', + version : '0.90', + meson_version : '>=0.51.0') + +cc = meson.get_compiler('c') +thread_dep = dependency('threads') +espeak_dep = dependency('espeak-ng') +alsa_dep = dependency('alsa') +math_dep = cc.find_library('m', required : false) + +subdir('doc') +subdir('services') +subdir('src') + +executable('espeakup', + espeakup_version, + espeakup_sources, + dependencies : [thread_dep, espeak_dep, alsa_dep, math_dep], + install : true) diff --git a/meson_options.txt b/meson_options.txt new file mode 100644 index 0000000..c8560b6 --- /dev/null +++ b/meson_options.txt @@ -0,0 +1,4 @@ +option('man', type : 'feature', value : 'auto', + description : 'build manpage with ronn') +option('systemd', type : 'feature', value : 'auto', + description :'enable systemd support') diff --git a/services/meson.build b/services/meson.build new file mode 100644 index 0000000..5eadcac --- /dev/null +++ b/services/meson.build @@ -0,0 +1,4 @@ +systemd = dependency('systemd') +if (systemd.found() and get_option('systemd').allowed()) or get_option('systemd').enabled() + subdir('systemd') +endif diff --git a/services/systemd/espeakup.service.in b/services/systemd/espeakup.service.in new file mode 100644 index 0000000..075a717 --- /dev/null +++ b/services/systemd/espeakup.service.in @@ -0,0 +1,18 @@ +[Unit] +Description=Software speech output for Speakup +Documentation=man:espeakup(8) +Wants=modprobe@speakup_soft.service +After=modprobe@speakup_soft.service sound.target + +[Service] +Type=forking +PIDFile=/run/espeakup.pid +Environment="default_voice=" +ExecStart=@bindir@/espeakup --default-voice=${default_voice} +ExecReload=kill -HUP $MAINPID +Restart=always +Nice=-10 +OOMScoreAdjust=-900 + +[Install] +WantedBy=sound.target diff --git a/services/systemd/meson.build b/services/systemd/meson.build new file mode 100644 index 0000000..1a15303 --- /dev/null +++ b/services/systemd/meson.build @@ -0,0 +1,20 @@ +if systemd.found() + unitdir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir') +else + unitdir = join_paths(prefixdir, get_option('libdir'), 'systemd', 'system') +endif +prefixdir = get_option('prefix') +bindir = join_paths(prefixdir, get_option('bindir')) + +unit_conf = configuration_data() +unit_conf.set('bindir', bindir) + +service_file = configure_file( + input : 'espeakup.service.in', + output : 'espeakup.service', + configuration : unit_conf +) + +install_data(service_file, + install_dir : unitdir +) diff --git a/cli.c b/src/cli.c similarity index 86% rename from cli.c rename to src/cli.c index 31db4b0..8453587 100644 --- a/cli.c +++ b/src/cli.c @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2008 William Hubbs * @@ -23,6 +23,8 @@ #include #include "espeakup.h" +#include "stringhandling.h" +#include "version.h" /* pid path */ extern char *pidPath; @@ -30,17 +32,20 @@ extern char *pidPath; /* default voice */ extern char *defaultVoice; +/* Whether to drive ALSA volume */ +extern int alsaVolume; + /* command line options */ const char *shortOptions = "P:V:adhv"; const struct option longOptions[] = { {"pid-path", required_argument, NULL, 'P'}, {"default-voice", required_argument, NULL, 'V'}, + {"alsa-volume", no_argument, &alsaVolume, 1}, {"acsint", no_argument, NULL, 'a'}, {"debug", no_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'v'}, - {0, 0, 0, 0} -}; + {0, 0, 0, 0}}; static void show_help() { @@ -48,6 +53,7 @@ static void show_help() printf("Options are as follows:\n"); printf(" --pid-path=path, -P path\t\tSet path for pid file.\n"); printf(" --default-voice=voice, -V voice\tSet default voice.\n"); + printf(" --alsa-volume\t\t\t\tDrive the ALSA volume.\n"); printf(" --debug, -d\t\t\t\tDebug mode (stay in the foreground).\n"); printf(" --help, -h\t\t\t\tShow this help.\n"); printf(" --version, -v\t\t\t\tDisplay the software version.\n"); @@ -67,18 +73,15 @@ static void show_version(void) void process_cli(int argc, char **argv) { int opt; - char *cp; do { opt = getopt_long(argc, argv, shortOptions, longOptions, NULL); switch (opt) { - case 'p': - cp = strdup(optarg); - if (cp != NULL) - pidPath = cp; + case 'P': + pidPath = dupeString(optarg); break; case 'V': - defaultVoice = strdup(optarg); + defaultVoice = dupeString(optarg); break; case 'a': espeakup_mode = ESPEAKUP_MODE_ACSINT; @@ -93,6 +96,7 @@ void process_cli(int argc, char **argv) show_version(); break; case -1: + case 0: break; default: show_help(); diff --git a/src/espeak.c b/src/espeak.c new file mode 100644 index 0000000..c9ac908 --- /dev/null +++ b/src/espeak.c @@ -0,0 +1,638 @@ +/* + * espeakup - interface which allows speakup to use espeak-ng + * + * Copyright (C) 2008 William Hubbs + * + * 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, see . + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "espeakup.h" + +/* default voice settings */ +const int defaultFrequency = 5; +const int defaultPitch = 5; +const int defaultRange = 5; +const int defaultRate = 2; +const int defaultVolume = 5; +char *defaultVoice = NULL; +int alsaVolume = 0; + +/* multipliers and offsets */ +const int frequencyMultiplier = 11; +const int pitchMultiplier = 11; +const int rangeMultiplier = 11; +const int rateMultiplier = 41; +const int rateOffset = 80; +const int volumeMultiplier = 22; + +volatile int stop_requested = 0; +int paused_espeak = 1; + +/* Wedged-engine detection. Espeak may legitimately refuse entries for a + * while (EE_BUFFER_FULL while a long backlog is being played back), so a + * failing entry is normally just retried. But when entries keep failing + * while the synth callback reports no progress at all, the audio output + * is most likely wedged (e.g. an ALSA device stuck returning EBUSY). + * After ESPEAK_STALL_RETRIES consecutive such retries (about one second + * each), restart the engine; after ESPEAK_MAX_RESTARTS restarts without + * the engine having been healthy for ESPEAK_HEALTHY_SECS in between, + * give up and exit, so that the init system can respawn us. */ +#define ESPEAK_STALL_RETRIES 10 +#define ESPEAK_MAX_RESTARTS 3 +#define ESPEAK_HEALTHY_SECS 60 + +/* Set by the synth callback whenever espeak makes synthesis progress; + * used to tell a merely backlogged engine from a wedged one. The + * callback runs in espeak's own thread, so the flag is atomic. */ +static atomic_int synth_progressed = 0; +static int stalled_retries = 0; +static int restart_attempts = 0; +static struct timespec last_restart; + +static int callback(short *wav, int numsamples, espeak_EVENT *events) +{ + int i; + atomic_store(&synth_progressed, 1); + for (i = 0; events[i].type != espeakEVENT_LIST_TERMINATED; i++) { + if (events[i].type == espeakEVENT_MARK) { + int mark = atoi(events[i].id.name); + if ((mark < 0) || (mark > 255)) + continue; + softsynth_reportindex(mark); + } + } + return 0; +} + +static espeak_ERROR set_frequency(struct synth_t *s, int freq, + enum adjust_t adj) +{ + espeak_ERROR rc; + + if (adj == ADJ_DEC) + freq = -freq; + if (adj != ADJ_SET) + freq += s->frequency; + rc = espeak_SetParameter(espeakRANGE, freq * frequencyMultiplier, 0); + if (rc == EE_OK) + s->frequency = freq; + return rc; +} + +static espeak_ERROR set_pitch(struct synth_t *s, int pitch, enum adjust_t adj) +{ + espeak_ERROR rc; + + if (adj == ADJ_DEC) + pitch = -pitch; + if (adj != ADJ_SET) + pitch += s->pitch; + rc = espeak_SetParameter(espeakPITCH, pitch * pitchMultiplier, 0); + if (rc == EE_OK) + s->pitch = pitch; + return rc; +} + +static espeak_ERROR set_range(struct synth_t *s, int range, enum adjust_t adj) +{ + espeak_ERROR rc; + + if (adj == ADJ_DEC) + range = -range; + if (adj != ADJ_SET) + range += s->range; + rc = espeak_SetParameter(espeakRANGE, range * rangeMultiplier, 0); + if (rc == EE_OK) + s->range = range; + return rc; +} + +static espeak_ERROR set_punctuation(struct synth_t *s, int punct, + enum adjust_t adj) +{ + espeak_ERROR rc; + espeak_PUNCT_TYPE espeak_punct; + + if (adj == ADJ_DEC) + punct = -punct; + if (adj != ADJ_SET) + punct += s->punct; + + switch (punct) { + case 0: + espeak_punct = espeakPUNCT_NONE; + break; + case 1: + espeak_punct = espeakPUNCT_SOME; + break; + case 2: + /* XXX: approximation */ + espeak_punct = espeakPUNCT_SOME; + break; + case 3: + default: + espeak_punct = espeakPUNCT_ALL; + break; + } + + rc = espeak_SetParameter(espeakPUNCTUATION, espeak_punct, 0); + if (rc == EE_OK) + s->punct = punct; + return rc; +} + +static espeak_ERROR set_rate(struct synth_t *s, int rate, enum adjust_t adj) +{ + espeak_ERROR rc; + + if (adj == ADJ_DEC) + rate = -rate; + if (adj != ADJ_SET) + rate += s->rate; + rc = espeak_SetParameter(espeakRATE, rate * rateMultiplier + rateOffset, 0); + if (rc == EE_OK) + s->rate = rate; + return rc; +} + +static espeak_ERROR set_voice(struct synth_t *s, char *voice) +{ + espeak_ERROR rc; + espeak_VOICE voice_select; + + rc = espeak_SetVoiceByName(voice); + if (rc != EE_OK) { + memset(&voice_select, 0, sizeof(voice_select)); + voice_select.languages = voice; + rc = espeak_SetVoiceByProperties(&voice_select); + } + if (rc == EE_OK) + strcpy(s->voice, voice); + return rc; +} + +static void set_alsa_volume(int vol) +{ + snd_mixer_t *m; + snd_mixer_elem_t *e; + int err; + + err = snd_mixer_open(&m, 0); + if (err < 0) { + fprintf(stderr, "ALSA mixer open error: %s\n", snd_strerror(err)); + return; + } + + err = snd_mixer_attach(m, "default"); + if (err < 0) { + fprintf(stderr, "ALSA mixer attach error: %s\n", snd_strerror(err)); + return; + } + err = snd_mixer_selem_register(m, NULL, NULL); + if (err < 0) { + fprintf(stderr, "ALSA mixer load error: %s\n", snd_strerror(err)); + return; + } + err = snd_mixer_load(m); + if (err < 0) { + fprintf(stderr, "ALSA mixer load error: %s\n", snd_strerror(err)); + return; + } + + /* Turn vol value to volume %. + * We do not want to soften that much with ALSA, espeak is already + * doing it. We want the default value (5) to be the usual default + * volume (80%), and make higher values increase ALSA volume, up to + * 100%. */ + + int volume = (vol + 1) * 50 / 10 + 50; + + for (e = snd_mixer_first_elem(m); e; e = snd_mixer_elem_next(e)) { + if (snd_mixer_elem_get_type(e) != SND_MIXER_ELEM_SIMPLE) + continue; + if (snd_mixer_selem_is_enumerated(e)) + continue; + + if (snd_mixer_selem_has_playback_switch(e)) { + snd_mixer_selem_set_playback_switch_all(e, 1); + } + + if (snd_mixer_selem_has_playback_volume(e)) { + long min, max, set; + + err = snd_mixer_selem_get_playback_dB_range(e, &min, &max); + if (err == 0 && min < max) { + if (max - min < 2400) { + /* 24dB amplitude is too small for using a logscale */ + set = min + volume * (max - min) / 100; + } else { + /* Use a logscale */ + double volf = volume / 100.; + if (min != SND_CTL_TLV_DB_GAIN_MUTE) { + double minf = pow(10, (min - max) / 6000.); + volf = volf * (1 - minf) + minf; + } + set = 6000. * log10(volf) + max; + } + snd_mixer_selem_set_playback_dB_all(e, set, 0); + } else { + /* No dB setting, try a linear scale */ + snd_mixer_selem_get_playback_volume_range(e, &min, &max); + set = min + volume * (max - min) / 100; + snd_mixer_selem_set_playback_volume_all(e, set); + } + } + } +} + +static espeak_ERROR set_volume(struct synth_t *s, int vol, enum adjust_t adj) +{ + espeak_ERROR rc; + + if (adj == ADJ_DEC) + vol = -vol; + if (adj != ADJ_SET) + vol += s->volume; + rc = espeak_SetParameter(espeakVOLUME, (vol + 1) * volumeMultiplier, 0); + if (rc == EE_OK) { + s->volume = vol; + if (alsaVolume) + set_alsa_volume(vol); + } + + return rc; +} + +static espeak_ERROR stop_speech(void) +{ + espeak_ERROR rc; + + rc = espeak_Cancel(); + return rc; +} + +static espeak_ERROR speak_text(struct synth_t *s) +{ + espeak_ERROR rc; + int synth_mode = 0; + + if (espeakup_mode == ESPEAKUP_MODE_ACSINT) + synth_mode |= espeakSSML; + + if (espeakup_mode == ESPEAKUP_MODE_SPEAKUP && (s->len == 1)) { + char *buf = NULL; + int n; + unsigned char c = s->buf[0]; + if (c == ' ') + n = asprintf(&buf, + " "); + else if (c < 0x20 || c > 0x7e) { + /* Not a printable character; do not embed it in SSML, as + * that would produce invalid markup. Fall through to the + * raw-synthesis path below. */ + n = -1; + } else { + /* Escape characters that are special in XML/SSML so the + * resulting markup stays well-formed; otherwise espeak-ng + * misparses the element, which is the source of + * the spurious high-pitched "ringing" on these characters. */ + const char *entity = NULL; + switch (c) { + case '<': entity = "<"; break; + case '>': entity = ">"; break; + case '&': entity = "&"; break; + case '\'': entity = "'"; break; + case '"': entity = """; break; + } + if (entity) + n = asprintf(&buf, + "%s", + entity); + else + n = asprintf(&buf, + "%c", + c); + } + if (n == -1) { + /* D'oh. Not much to do on allocation failure. + * Perhaps espeak will happen to say the character */ + rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, + synth_mode, NULL, NULL); + } else { + rc = espeak_Synth(buf, n + 1, 0, POS_CHARACTER, 0, espeakSSML, NULL, + NULL); + free(buf); + } + } else + rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, synth_mode, + NULL, NULL); + return rc; +} + +static void free_espeak_entry(struct espeak_entry_t *entry) +{ + assert(entry); + if (entry->cmd == CMD_SPEAK_TEXT) + free(entry->buf); + free(entry); +} + +static void synth_queue_clear() +{ + struct espeak_entry_t *current; + + while (queue_peek(synth_queue)) { + current = (struct espeak_entry_t *) queue_remove(synth_queue); + free_espeak_entry(current); + } +} + +static int reinitialize_espeak(struct synth_t *s) +{ + int rate; + + /* Re-initialize espeak */ + rate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0); + if (rate < 0) { + fprintf(stderr, "Unable to initialize espeak.\n"); + return -1; + } + + espeak_SetSynthCallback(callback); + + /* Set parameters again */ + espeak_SetVoiceByName(s->voice); + espeak_SetParameter(espeakRANGE, s->frequency * frequencyMultiplier, 0); + espeak_SetParameter(espeakPITCH, s->pitch * pitchMultiplier, 0); + espeak_SetParameter(espeakRATE, s->rate * rateMultiplier + rateOffset, 0); + espeak_SetParameter(espeakVOLUME, (s->volume + 1) * volumeMultiplier, 0); + espeak_SetParameter(espeakCAPITALS, 0, 0); + paused_espeak = 0; + return 0; +} + +/* Wait for up to a second before retrying an entry which could not be + * processed, so that we do not busy-loop on a persistent error. Called + * and returns with queue_guard held. Wakes up immediately if a stop is + * requested. */ +static void espeak_wait_retry(void) +{ + struct timespec timeout; + + clock_gettime(CLOCK_MONOTONIC, &timeout); + timeout.tv_sec++; + pthread_cond_timedwait(&wake_stop, &queue_guard, &timeout); +} + +/* Handle an entry which could not be processed. Called and returns + * with queue_guard held. + * Normally just back off before the retry, but watch out for a wedged + * engine: if entries keep failing while the synth callback shows no + * progress at all, restart the engine, and if restarting does not help + * either, exit so that the init system respawns us in a clean state. */ +static void espeak_handle_failure(struct synth_t *s) +{ + if (atomic_exchange(&synth_progressed, 0)) { + /* Espeak is making progress, it is merely backlogged. */ + stalled_retries = 0; + } else if (++stalled_retries >= ESPEAK_STALL_RETRIES) { + stalled_retries = 0; + if (++restart_attempts > ESPEAK_MAX_RESTARTS) { + fprintf(stderr, "espeakup: espeak keeps failing without " + "making progress and restarting it did not help, " + "aborting\n"); + /* Use _exit because exit could hang in library destructors + * while the audio device is wedged. */ + _exit(3); + } + fprintf(stderr, "espeakup: espeak has been failing without " + "making progress for %d seconds, restarting it\n", + ESPEAK_STALL_RETRIES); + /* Call into espeak with queue_guard released: these calls can + * take time, or block on a wedged audio device. If they do + * block forever, the stop-acknowledgement timeout in the + * softsynth thread is our last resort. */ + pthread_mutex_unlock(&queue_guard); + if (!paused_espeak) { + espeak_Cancel(); + espeak_Terminate(); + paused_espeak = 1; + } + reinitialize_espeak(s); + clock_gettime(CLOCK_MONOTONIC, &last_restart); + pthread_mutex_lock(&queue_guard); + return; + } + + espeak_wait_retry(); +} + +static struct espeak_entry_t *current = NULL; +static void queue_process_entry(struct synth_t *s) +{ + espeak_ERROR error = EE_OK; + char markbuff[50]; + + if (current != queue_peek(synth_queue)) { + if (current) + free_espeak_entry(current); + current = queue_peek(synth_queue); + } + pthread_mutex_unlock(&queue_guard); + + if (current->cmd != CMD_PAUSE && paused_espeak) { + if (reinitialize_espeak(s) < 0) { + /* Espeak is unavailable, so the entry cannot be processed. + * Calling espeak functions on a terminated engine would + * just fail (or worse). Leave the entry queued and retry + * after a small pause. */ + pthread_mutex_lock(&queue_guard); + espeak_handle_failure(s); + return; + } + } + + switch (current->cmd) { + case CMD_SET_FREQUENCY: + error = set_frequency(s, current->value, current->adjust); + break; + case CMD_SET_MARK: + snprintf(markbuff, sizeof(markbuff), "", + current->value); + error = espeak_Synth(markbuff, strlen(markbuff) + 1, 0, POS_CHARACTER, + 0, espeakSSML, NULL, NULL); + break; + case CMD_SET_PITCH: + error = set_pitch(s, current->value, current->adjust); + break; + case CMD_SET_RANGE: + error = set_range(s, current->value, current->adjust); + break; + case CMD_SET_PUNCTUATION: + error = set_punctuation(s, current->value, current->adjust); + break; + case CMD_SET_RATE: + error = set_rate(s, current->value, current->adjust); + break; + case CMD_SET_VOICE: + error = EE_OK; + break; + case CMD_SET_VOLUME: + error = set_volume(s, current->value, current->adjust); + break; + case CMD_SPEAK_TEXT: + s->buf = current->buf; + s->len = current->len; + error = speak_text(s); + break; + case CMD_PAUSE: + if (!paused_espeak) { + error = espeak_Cancel(); + if (error == EE_OK) + error = espeak_Terminate(); + if (error == EE_OK) + paused_espeak = 1; + } else { + error = EE_OK; + } + break; + default: + /* Uh? */ + error = EE_OK; + break; + } + + pthread_mutex_lock(&queue_guard); + if (error == EE_OK) { + /* Processed, drop it */ + struct espeak_entry_t *unqueued = queue_remove(synth_queue); + assert(unqueued == current); + free_espeak_entry(current); + current = NULL; + stalled_retries = 0; + if (restart_attempts) { + /* Forget about past restarts once the engine has been + * healthy for a while. Entries can spuriously succeed + * right after a restart while the audio output is still + * wedged (espeak's internal queue is empty again), so a + * quick success must not reset the counter. */ + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + if (now.tv_sec - last_restart.tv_sec >= ESPEAK_HEALTHY_SECS) + restart_attempts = 0; + } + } else { + if (error != EE_BUFFER_FULL) + fprintf(stderr, "espeak error: %d\n", error); + /* The entry stays queued and will be retried. Give espeak a + * little break before that, whatever the error: previously only + * EE_BUFFER_FULL throttled, and any other persistent error made + * us retry the same entry in a tight loop, burning a whole CPU. */ + espeak_handle_failure(s); + } +} + +int initialize_espeak(struct synth_t *s) +{ + int rate; + + /* initialize espeak */ + rate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0); + if (rate < 0) { + fprintf(stderr, "Unable to initialize espeak.\n"); + return -1; + } + + espeak_SetSynthCallback(callback); + + /* Setup initial voice parameters */ + if (defaultVoice && defaultVoice[0]) { + set_voice(s, defaultVoice); + free(defaultVoice); + defaultVoice = NULL; + } + set_frequency(s, defaultFrequency, ADJ_SET); + set_pitch(s, defaultPitch, ADJ_SET); + set_range(s, defaultRange, ADJ_SET); + set_rate(s, defaultRate, ADJ_SET); + set_volume(s, defaultVolume, ADJ_SET); + espeak_SetParameter(espeakCAPITALS, 0, 0); + paused_espeak = 0; + return 0; +} + +/* espeak_thread is the "main" function of our secondary (queue-processing) + * thread. + * First, lock queue_guard, because it needs to be locked when we call + * pthread_cond_wait on the runner_awake condition variable. + * Next, enter an infinite loop. + * The wait call also unlocks queue_guard, so that the other thread can + * manipulate the queue. + * When runner_awake is signaled, the pthread_cond_wait call re-locks + * queue_guard, and the "queue processor" thread has access to the queue. + * While there is an entry in the queue, call queue_process_entry. + * queue_process_entry unlocks queue_guard after removing an item from the + * queue, so that the main thread doesn't have to wait for us to finish + * processing the entry. So re-lock queue_guard after each call to + * queue_process_entry. + * + * The main thread can add items to the queue in exactly two situations: + * 1. We are waiting on runner_awake, or + * 2. We are processing an entry that has just been removed from the queue. + */ +void *espeak_thread(void *arg) +{ + struct synth_t *s = (struct synth_t *) arg; + + pthread_mutex_lock(&queue_guard); + while (should_run) { + while (should_run && !queue_peek(synth_queue) && !stop_requested) + pthread_cond_wait(&runner_awake, &queue_guard); + + if (stop_requested) { + current = NULL; + /* Call into espeak with queue_guard released: espeak_Cancel + * can take time, or even block indefinitely when the audio + * output is wedged, and holding the lock here would prevent + * the other threads from ever making progress again. The + * queue cannot change concurrently: the only producer (the + * softsynth thread) is blocked waiting for stop_acknowledged + * as long as stop_requested is set. */ + pthread_mutex_unlock(&queue_guard); + stop_speech(); + pthread_mutex_lock(&queue_guard); + synth_queue_clear(); + stop_requested = 0; + pthread_cond_signal(&stop_acknowledged); + } + + while (should_run && queue_peek(synth_queue) && !stop_requested) { + queue_process_entry(s); + } + } + pthread_cond_signal(&stop_acknowledged); + pthread_mutex_unlock(&queue_guard); + return NULL; +} diff --git a/espeakup.c b/src/espeakup.c similarity index 74% rename from espeakup.c rename to src/espeakup.c index 1981430..4e077f5 100644 --- a/espeakup.c +++ b/src/espeakup.c @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2008 William Hubbs * @@ -18,17 +18,18 @@ */ #include +#include #include #include #include #include -#include -#include #include +#include +#include #include "espeakup.h" -/* path to our pid file */ +// path to our pid file char *pidPath = "/var/run/espeakup.pid"; int debug = 0; @@ -40,7 +41,9 @@ volatile int should_run = 1; espeak_AUDIO_OUTPUT audio_mode; pthread_cond_t runner_awake = PTHREAD_COND_INITIALIZER; -pthread_cond_t stop_acknowledged = PTHREAD_COND_INITIALIZER; +/* Initialized in main: use the monotonic clock for timed waits. */ +pthread_cond_t wake_stop; +pthread_cond_t stop_acknowledged; pthread_mutex_t queue_guard = PTHREAD_MUTEX_INITIALIZER; int espeakup_start_daemon(void) @@ -60,7 +63,7 @@ int espeakup_start_daemon(void) exit(1); } if (pid) { - /* Parent, just wait for daemon */ + // Parent, just wait for daemon if (read(fds[0], &c, 1) < 0) { printf("Espeakup is already running!\n"); exit(1); @@ -68,17 +71,17 @@ int espeakup_start_daemon(void) exit(c); } - /* Child, create new session */ + // Child, create new session setsid(); pid = fork(); if (pid) - /* Intermediate child, just exit */ + // Intermediate child, just exit exit(0); - /* Child */ + // Child if (chdir("/") < 0) { c = 1; - (void)write(fds[1], &c, 1); + (void) write(fds[1], &c, 1); exit(1); } return fds[1]; @@ -99,20 +102,18 @@ int espeakup_is_running(void) } if (flock(pidFile, LOCK_EX) < 0) { - printf("Can not lock the pid file %s: %s\n", pidPath, - strerror(errno)); + printf("Can not lock the pid file %s: %s\n", pidPath, strerror(errno)); goto error; } n = read(pidFile, s, sizeof(s) - 1); if (n < 0) { - printf("Can not read the pid file %s: %s\n", pidPath, - strerror(errno)); + printf("Can not read the pid file %s: %s\n", pidPath, strerror(errno)); goto error; } s[n] = 0; n = sscanf(s, "%d", &pid); if (n == 1 && (!kill(pid, 0) || errno != ESRCH)) { - /* Already running */ + // Already running close(pidFile); return 1; } @@ -148,6 +149,18 @@ int main(int argc, char **argv) struct synth_t s = { .voice = "", }; + pthread_condattr_t monotonic_attr; + + /* Condition variables used with pthread_cond_timedwait must use the + * monotonic clock, so that wall-clock adjustments (NTP, an + * installer setting the system time) cannot make the timeouts fire + * too early or far too late. */ + pthread_condattr_init(&monotonic_attr); + pthread_condattr_setclock(&monotonic_attr, CLOCK_MONOTONIC); + pthread_cond_init(&wake_stop, &monotonic_attr); + pthread_cond_init(&stop_acknowledged, &monotonic_attr); + pthread_condattr_destroy(&monotonic_attr); + synth_queue = new_queue(); if (!synth_queue) { @@ -155,13 +168,13 @@ int main(int argc, char **argv) return 2; } - /* set up the pipe used to wake the espeak thread */ + // set up the pipe used to wake the espeak thread if (pipe(self_pipe_fds) < 0) { perror("Unable to create pipe"); return 5; } - /* process command line options */ + // process command line options process_cli(argc, argv); if (!debug && espeakup_mode == ESPEAKUP_MODE_SPEAKUP) { @@ -181,7 +194,7 @@ int main(int argc, char **argv) close(devnull); } - /* create the signal processing thread here. */ + // create the signal processing thread here. err = pthread_create(&signal_thread_id, NULL, signal_thread, NULL); if (err != 0) { ret = 4; @@ -197,26 +210,26 @@ int main(int argc, char **argv) sigaddset(&sigset, SIGTERM); sigprocmask(SIG_BLOCK, &sigset, NULL); -/* Initialize espeak */ + // Initialize espeak if (initialize_espeak(&s) < 0) { ret = 2; goto out; } -/* open the softsynth */ + // open the softsynth if (open_softsynth() < 0) { ret = 2; goto out; } - /* Spawn our softsynth thread. */ + // Spawn our softsynth thread. err = pthread_create(&softsynth_thread_id, NULL, softsynth_thread, &s); if (err != 0) { ret = 4; goto out; } - /* Spawn our espeak-interacting thread. */ + // Spawn our espeak-interacting thread. err = pthread_create(&espeak_thread_id, NULL, espeak_thread, &s); if (err != 0) { ret = 4; @@ -224,14 +237,15 @@ int main(int argc, char **argv) } if (!debug && espeakup_mode == ESPEAKUP_MODE_SPEAKUP) - (void)write(fd, &ret, 1); + (void) write(fd, &ret, 1); - /* wait for the threads to shut down. */ + // wait for the threads to shut down. pthread_join(signal_thread_id, NULL); pthread_join(softsynth_thread_id, NULL); pthread_join(espeak_thread_id, NULL); - espeak_Terminate(); + if (!paused_espeak) + espeak_Terminate(); close_softsynth(); out: @@ -239,9 +253,8 @@ out: if (ret != 1) unlink(pidPath); if (ret != 0) - (void)write(fd, &ret, 1); - /* If ret was 0, the status byte was written before joining - * the threads. */ + (void) write(fd, &ret, 1); + // If ret was 0, the status byte was written before joining the threads. } return ret; } diff --git a/espeakup.h b/src/espeakup.h similarity index 81% rename from espeakup.h rename to src/espeakup.h index 4d1bcf7..62a5ca8 100644 --- a/espeakup.h +++ b/src/espeakup.h @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2008 William Hubbs * @@ -20,35 +20,40 @@ #ifndef __ESPEAKUP_H #define __ESPEAKUP_H -/* This was added for gcc 4.3 */ -#include +// This was added for gcc 4.3 #include +#include -#include +#include #include "queue.h" -#define PACKAGE_VERSION "0.80" -#define PACKAGE_BUGREPORT "http://github.com/williamh/espeakup/issues" +#define PACKAGE_BUGREPORT "https://github.com/linux-speakup/espeakup/issues" -enum espeakup_mode_t { +enum espeakup_mode_t +{ ESPEAKUP_MODE_SPEAKUP, ESPEAKUP_MODE_ACSINT }; -enum command_t { +enum command_t +{ CMD_SET_FREQUENCY, + CMD_SET_MARK, CMD_SET_PITCH, + CMD_SET_RANGE, CMD_SET_PUNCTUATION, CMD_SET_RATE, CMD_SET_VOICE, CMD_SET_VOLUME, CMD_SPEAK_TEXT, CMD_FLUSH, + CMD_PAUSE, CMD_UNKNOWN, }; -enum adjust_t { +enum adjust_t +{ ADJ_DEC, ADJ_SET, ADJ_INC, @@ -65,9 +70,10 @@ struct espeak_entry_t { struct synth_t { int frequency; int pitch; + int range; int punct; int rate; - char voice[10]; + char voice[20]; int volume; char *buf; int len; @@ -84,13 +90,16 @@ extern void *espeak_thread(void *arg); extern int open_softsynth(void); extern void close_softsynth(void); extern void *softsynth_thread(void *arg); +extern void softsynth_reportindex(int index); extern volatile int should_run; extern volatile int stop_requested; +extern int paused_espeak; extern int self_pipe_fds[2]; #define PIPE_READ_FD (self_pipe_fds[0]) #define PIPE_WRITE_FD (self_pipe_fds[1]) extern pthread_cond_t runner_awake; +extern pthread_cond_t wake_stop; extern pthread_cond_t stop_acknowledged; extern pthread_mutex_t queue_guard; diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..6221208 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,10 @@ +espeakup_sources = files([ + 'cli.c', + 'espeak.c', + 'espeakup.c', + 'queue.c', + 'signal.c', + 'softsynth.c', + 'stringhandling.c' +]) +espeakup_version = vcs_tag(input : 'version.h.in', output : 'version.h') diff --git a/queue.c b/src/queue.c similarity index 96% rename from queue.c rename to src/queue.c index 860ad94..63a5527 100644 --- a/queue.c +++ b/src/queue.c @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Note that these functions are meant to be used in either a single or * multi-threaded environment, so they know nothing about mutexes, etc. diff --git a/queue.h b/src/queue.h similarity index 90% rename from queue.h rename to src/queue.h index 249f3ba..a1a2a99 100644 --- a/queue.h +++ b/src/queue.h @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2008 William Hubbs * @@ -20,7 +20,7 @@ #ifndef __QUEUE_H #define __QUEUE_H -struct queue_t; /* An opaque type. */ +struct queue_t; // An opaque type. extern struct queue_t *new_queue(void); extern int queue_add(struct queue_t *q, void *entry); diff --git a/signal.c b/src/signal.c similarity index 70% rename from signal.c rename to src/signal.c index 102c3cf..b6ddefc 100644 --- a/signal.c +++ b/src/signal.c @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2008 William Hubbs * @@ -21,10 +21,11 @@ #include #include #include -#define STOP_MSG "s" #include "espeakup.h" +#define STOP_MSG "s" + /* * We install a dummy signal handler to let the o/s know that we * do not want the default action to be performed since we are @@ -40,8 +41,8 @@ void *signal_thread(void *arg) sigset_t sigset; int sig; - memset(&temp, 0, sizeof (struct sigaction)); - /* install dummy handlers for the signals we want to process */ + memset(&temp, 0, sizeof(struct sigaction)); + // install dummy handlers for the signals we want to process temp.sa_handler = dummy_handler; sigemptyset(&temp.sa_mask); sigaction(SIGINT, &temp, NULL); @@ -57,14 +58,24 @@ void *signal_thread(void *arg) case SIGTERM: pthread_mutex_lock(&queue_guard); should_run = 0; + /* Wake up any thread waiting on a condition variable so + * that it notices the shutdown request: the softsynth + * thread may be waiting for a stop acknowledgement, and + * the espeak thread may be waiting for work or throttling + * before a retry. */ + pthread_cond_broadcast(&runner_awake); + pthread_cond_broadcast(&wake_stop); + pthread_cond_broadcast(&stop_acknowledged); pthread_mutex_unlock(&queue_guard); break; default: printf("espeakup caught signal %d\n", sig); break; } + pthread_mutex_lock(&queue_guard); } - /* Tell the reader to stop, if it is in a select() call. */ + pthread_mutex_unlock(&queue_guard); + // Tell the reader to stop, if it is in a select() call. write(PIPE_WRITE_FD, STOP_MSG, strlen(STOP_MSG)); return NULL; } diff --git a/softsynth.c b/src/softsynth.c similarity index 70% rename from softsynth.c rename to src/softsynth.c index 3394434..e382b32 100644 --- a/softsynth.c +++ b/src/softsynth.c @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2008 William Hubbs * @@ -17,27 +17,28 @@ * along with this program. If not, see . */ +#include #include #include #include #include -#include -#include #include -#include +#include +#include +#include #include "espeakup.h" #include "stringhandling.h" -/* max buffer size */ +// max buffer size static const size_t maxBufferSize = 16 * 1024 + 1; -/* synth flush character */ +// synth flush character static const int synthFlushChar = 0x18; static int softFD = 0; -/* Text accumulator: */ +// Text accumulator: char *textAccumulator; int textAccumulator_l; @@ -124,15 +125,24 @@ static int process_command(struct synth_t *s, char *buf, int start) case 'f': cmd = CMD_SET_FREQUENCY; break; + case 'i': + cmd = CMD_SET_MARK; + break; case 'p': cmd = CMD_SET_PITCH; break; + case 'r': + cmd = CMD_SET_RANGE; + break; case 's': cmd = CMD_SET_RATE; break; case 'v': cmd = CMD_SET_VOLUME; break; + case 'P': + cmd = CMD_PAUSE; + break; default: cmd = CMD_UNKNOWN; break; @@ -146,8 +156,7 @@ static int process_command(struct synth_t *s, char *buf, int start) } if (cmd != CMD_FLUSH && cmd != CMD_UNKNOWN) { - if (espeakup_mode == ESPEAKUP_MODE_ACSINT - && textAccumulator_l != 0) { + if (espeakup_mode == ESPEAKUP_MODE_ACSINT && textAccumulator_l != 0) { queue_add_text(textAccumulator, textAccumulator_l); free(textAccumulator); textAccumulator = initString(&textAccumulator_l); @@ -168,7 +177,8 @@ static void process_buffer(struct synth_t *s, char *buf, ssize_t length) start = 0; end = 0; while (start < length) { - while ((buf[end] < 0 || buf[end] >= ' ') && end < length) + while ((buf[end] < 0 || buf[end] >= ' ' || buf[end] == '\n') && + end < length) end++; if (end != start) { txtLen = end - start; @@ -183,8 +193,7 @@ static void process_buffer(struct synth_t *s, char *buf, ssize_t length) } } -static void process_buffer_acsint(struct synth_t *s, char *buf, - ssize_t length) +static void process_buffer_acsint(struct synth_t *s, char *buf, ssize_t length) { int start = 0; int i; @@ -198,8 +207,8 @@ static void process_buffer_acsint(struct synth_t *s, char *buf, break; } if (i > start) - stringAndBytes(&textAccumulator, &textAccumulator_l, - buf + start, i - start); + stringAndBytes(&textAccumulator, &textAccumulator_l, buf + start, + i - start); if (flushIt) { if (textAccumulator != EMPTYSTRING) { queue_add_text(textAccumulator, textAccumulator_l); @@ -215,29 +224,56 @@ static void process_buffer_acsint(struct synth_t *s, char *buf, } } +/* How long to wait for the espeak thread to acknowledge a stop request + * before concluding that espeak is wedged beyond in-process recovery. */ +static const int stopAckTimeout = 10; + static void request_espeak_stop(void) { + struct timespec timeout; + int err = 0; + pthread_mutex_lock(&queue_guard); stop_requested = 1; - pthread_cond_signal(&runner_awake); /* Wake runner, if necessary. */ - while (should_run && stop_requested) - pthread_cond_wait(&stop_acknowledged, &queue_guard); /* wait for acknowledgement. */ + pthread_cond_signal(&runner_awake); // Wake runner, if necessary. + pthread_cond_signal(&wake_stop); // Wake runner, if necessary. + clock_gettime(CLOCK_MONOTONIC, &timeout); + timeout.tv_sec += stopAckTimeout; + while (should_run && stop_requested && err != ETIMEDOUT) + // wait for acknowledgement. + err = pthread_cond_timedwait(&stop_acknowledged, &queue_guard, + &timeout); + if (should_run && stop_requested) { + /* The espeak thread is stuck in a call into espeak, most likely + * on a wedged audio device. There is no way to recover from + * within the process: exit so that the init system can respawn + * us in a clean state, rather than staying silent, ignoring + * SIGTERM, and stalling the whole console by not draining + * /dev/softsynth anymore. Use _exit because exit could hang in + * library destructors while the audio device is wedged. */ + fprintf(stderr, "espeakup: espeak did not acknowledge a stop " + "request within %d seconds, aborting\n", stopAckTimeout); + _exit(3); + } pthread_mutex_unlock(&queue_guard); } int open_softsynth(void) { int rc = 0; - /* If we're in acsint mode, we read from stdin. No need to open. */ + // If we're in acsint mode, we read from stdin. No need to open. if (espeakup_mode == ESPEAKUP_MODE_ACSINT) { softFD = STDIN_FILENO; return 0; } - /* open the softsynth. */ - softFD = open("/dev/softsynth", O_RDWR | O_NONBLOCK); + // open the softsynth. + softFD = open("/dev/softsynthu", O_RDWR | O_NONBLOCK); + if (softFD < 0 && errno == ENOENT) + // Kernel without unicode support? Try without unicode. + softFD = open("/dev/softsynth", O_RDWR | O_NONBLOCK); if (softFD < 0) { - perror("Unable to open the softsynth device"); + perror("Unable to open the /dev/softsynth device"); rc = -1; } return rc; @@ -319,3 +355,16 @@ void *softsynth_thread(void *arg) pthread_mutex_unlock(&queue_guard); return NULL; } + +void softsynth_reportindex(int index) +{ + if (espeakup_mode == ESPEAKUP_MODE_ACSINT) { + putchar(index); + fflush(stdout); + } else { + char buf[16]; + snprintf(buf, sizeof(buf), "%d", index); + if (write(softFD, buf, strlen(buf)) < 0) + perror("Writing index failed"); + } +} diff --git a/stringhandling.c b/src/stringhandling.c similarity index 88% rename from stringhandling.c rename to src/stringhandling.c index 8c18a39..096b870 100644 --- a/stringhandling.c +++ b/src/stringhandling.c @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2011 William Hubbs * @@ -62,6 +62,17 @@ void *reallocMem(void *p, size_t n) return s; } +char *dupeString(char *s) +{ + char *c; + + if (!(c = strdup(s))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + return c; +} + char *initString(int *l) { *l = 0; @@ -75,9 +86,9 @@ void stringAndString(char **s, int *l, const char *t) oldlen = *l; newlen = oldlen + strlen(t); *l = newlen; - ++newlen; /* room for the 0 */ + ++newlen; // room for the 0 x = oldlen ^ newlen; - if (x > oldlen) { /* must realloc */ + if (x > oldlen) { // must realloc newlen |= (newlen >> 1); newlen |= (newlen >> 2); newlen |= (newlen >> 4); @@ -98,7 +109,7 @@ void stringAndBytes(char **s, int *l, const char *t, int cnt) *l = newlen; ++newlen; x = oldlen ^ newlen; - if (x > oldlen) { /* must realloc */ + if (x > oldlen) { // must realloc newlen |= (newlen >> 1); newlen |= (newlen >> 2); newlen |= (newlen >> 4); diff --git a/stringhandling.h b/src/stringhandling.h similarity index 91% rename from stringhandling.h rename to src/stringhandling.h index a7d7cea..7f104ef 100644 --- a/stringhandling.h +++ b/src/stringhandling.h @@ -1,5 +1,5 @@ /* - * espeakup - interface which allows speakup to use espeak + * espeakup - interface which allows speakup to use espeak-ng * * Copyright (C) 2011 William Hubbs * @@ -26,6 +26,7 @@ extern char *EMPTYSTRING; void *allocMem(size_t n); void *reallocMem(void *p, size_t n); +char *dupeString(char *s); char *initString(int *l); void stringAndString(char **s, int *l, const char *t); void stringAndBytes(char **s, int *l, const char *t, int cnt); diff --git a/src/version.h.in b/src/version.h.in new file mode 100644 index 0000000..9ba36a9 --- /dev/null +++ b/src/version.h.in @@ -0,0 +1,25 @@ +/* + * espeakup - interface which allows speakup to use espeak-ng + * + * Copyright (C) 2008 William Hubbs + * + * 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, see . + */ + +#ifndef __VERSION_H +#define __VERSION_H + +#define PACKAGE_VERSION "@VCS_TAG@" + +#endif