# HG changeset patch # User Markus Bröker # Date 1229180199 -3600 # Node ID 06dd3b8d90ad2fc37cc866509aa5dda7e58a0678 Virtual Reader committer: Markus Bröker diff --git a/Makefile b/Makefile new file mode 100644 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +TARGETS=player src + +all: + @for i in $(TARGETS); \ + do \ + make --no-print-directory -C $$i all; \ + done + +.PHONY: clean distclean + +clean: + @for i in $(TARGETS); \ + do \ + make --no-print-directory -C $$i clean; \ + done + @rm -f *~ include/*~ + +distclean: + @for i in $(TARGETS); \ + do \ + make --no-print-directory -C $$i distclean; \ + done + diff --git a/README b/README new file mode 100644 --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +Projekt VirtualReader + +Authors : Klaus_Dieter, bordi, mbroeker +Maintainer : mbroeker + +LINKS: +http://www.atip.de/index.php?option=com_content&task=view&id=46&Itemid=74 +MBrola Project: http://tcts.fpms.ac.be/synthesis/mbrola.html + - http://www.ikp.uni-bonn.de/dt/forsch/phonetik/hadifix/txt2pho.zip +Festival Synth: http://www.cstr.ed.ac.uk/projects/festival + - install festival and copy text2wave to /usr/local/bin + +Requirements: + 1. MBROLA, FESTIVAL or Proser TTS-System + 2. txt2phpo, preproc and pipeflt for mbrola, + text2wave for festival + 3. SDL-1.2.x + +Directory Structure for /opt/mbrola +drwxr-xr-x 2 root root 144 2007-12-20 09:24 bin +drwxr-xr-x 2 root root 840 2007-08-23 17:56 data +drwxr-xr-x 3 root root 152 2002-11-07 11:48 de5 +drwxr-xr-x 2 root root 80 2007-08-23 17:54 doc +-rw-r--r-- 1 root root 5836 2007-08-23 17:57 Rules.lst diff --git a/audio/.keep b/audio/.keep new file mode 100644 diff --git a/bin/festival_write_wav b/bin/festival_write_wav new file mode 100755 --- /dev/null +++ b/bin/festival_write_wav @@ -0,0 +1,9 @@ +#!/bin/bash + +file=$1 + +eval "cat $file |\ +text2wave - -o $2" + + + diff --git a/bin/mbrola_write_wav b/bin/mbrola_write_wav new file mode 100755 --- /dev/null +++ b/bin/mbrola_write_wav @@ -0,0 +1,13 @@ +#!/bin/bash + +ROOT=/opt/mbrola # Where are the needed files? +VOICE=$ROOT/de5/de5 # Path to the mbrola-voice +SEX=f # m/f Which sex has your voice? + +file=$1 + +eval "cat $file |\ +$ROOT/bin/pipefilt |\ +$ROOT/bin/preproc $ROOT/Rules.lst $ROOT/data/hadifix.abk |\ +$ROOT/bin/txt2pho -$SEX -p $ROOT/data/ |\ +$ROOT/bin/mbrola $VOICE - $2" diff --git a/include/MBrola.h b/include/MBrola.h new file mode 100644 --- /dev/null +++ b/include/MBrola.h @@ -0,0 +1,9 @@ +/** + * $Id: MBrola.h 48 2008-01-09 23:59:19Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/MBrola.h $ + */ + +#ifndef MBROLA_H +#define MBROLA_H + +#endif diff --git a/include/audioplayer.h b/include/audioplayer.h new file mode 100644 --- /dev/null +++ b/include/audioplayer.h @@ -0,0 +1,54 @@ +/** + * $Id: audioplayer.h 48 2008-01-09 23:59:19Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/audioplayer.h $ + */ + +#ifndef __AUDIOPLAYER_H__ +#define __AUDIOPLAYER_H__ + +#include +#include +#include +#include +#include +#include + +#include + +/* audioplayer(filename, milliseconds, position) */ +int audioplayer (char *, Uint32); + +/* initializes the sdl-soundsystem and prepares local structures */ +int audio_init (); + +/* closes the sdl-soundsystem and frees local structures */ +void audio_shutdown (); + +/* audioplayer_getwavelength(filename) */ +Uint32 audioplayer_getwavelength (char *); + +/* returns the bitrate of the wave-file */ +int audioplayer_getbitrate (); + +/* stopps playback immediately */ +void audioplayer_stop (); + +/* returns the current time in ms */ +Uint32 audioplayer_gettime (); + +/* sets the current position in ms */ +void audioplayer_settime (long); + +/* returns the length of the RIFF in ms */ +Uint32 audioplayer_getsoundlen (); + +/* returns the current position */ +Uint32 audioplayer_getposition (); + +/* wait replaces sdl_delay */ +void audioplayer_delay (Uint32); + +/* switches verbosity on/off */ +void audioplayer_setverbose (int); + +#endif diff --git a/include/festival_interface.h b/include/festival_interface.h new file mode 100644 --- /dev/null +++ b/include/festival_interface.h @@ -0,0 +1,31 @@ +/** + * $Id: festival_interface.h 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/festival_interface.h $ + */ + +#ifndef __FESTIVAL_INTERFACE_H__ +#define __FESTIVAL_INTERFACE_H__ + +#include +#include +#include +#include +#include + +typedef struct { + char *TextFile; + char *AudioFile; + char *Voice; + char *Path; +} tts_options; + +typedef struct { +} ttshandles; + +tts_options interface_get_cl_opts (int, char **); + +int interface_write_to_wav (char *, char *, ttshandles, tts_options); +ttshandles interface_init (tts_options); +long *interface_get_timing (ttshandles, char *); + +#endif diff --git a/include/interface.h b/include/interface.h new file mode 100644 --- /dev/null +++ b/include/interface.h @@ -0,0 +1,21 @@ +/** + * $Id: interface.h 48 2008-01-09 23:59:19Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/interface.h $ + */ + +#ifndef INTERFACE_H +#define INTERFACE_H + +#ifdef PROSER +#include +#endif + +#ifdef MBROLA +#include +#endif + +#ifdef FESTIVAL +#include +#endif + +#endif diff --git a/include/keyboard.h b/include/keyboard.h new file mode 100644 --- /dev/null +++ b/include/keyboard.h @@ -0,0 +1,18 @@ +/** + * $Id: keyboard.h 48 2008-01-09 23:59:19Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/keyboard.h $ + */ + +#ifndef __KEYBOARD_H__ +#define __KEYBOARD_H__ + +#include +#include +#include +#ifdef DEBUG +#include +#endif + +char getSingleKey (void); + +#endif diff --git a/include/mbrola_interface.h b/include/mbrola_interface.h new file mode 100644 --- /dev/null +++ b/include/mbrola_interface.h @@ -0,0 +1,31 @@ +/** + * $Id: mbrola_interface.h 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/mbrola_interface.h $ + */ + +#ifndef __MBROLA_INTERFACE_H__ +#define __MBROLA_INTERFACE_H__ + +#include +#include +#include +#include +#include +#include + +typedef struct { + char *TextFile; + char *AudioFile; + char *Voice; + char *Path; +} tts_options; + +typedef struct { +} ttshandles; + +tts_options interface_get_cl_opts (int, char **); + +int interface_write_to_wav (char *, char *, ttshandles, tts_options); +ttshandles interface_init (tts_options); +long *interface_get_timing (ttshandles, char *); +#endif diff --git a/include/proser_interface.h b/include/proser_interface.h new file mode 100644 --- /dev/null +++ b/include/proser_interface.h @@ -0,0 +1,37 @@ +/** + * $Id: proser_interface.h 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/proser_interface.h $ + */ + +#ifndef __PROSER_INTERFACE_H__ +#define __PROSER_INTERFACE_H__ + +#include +#include +#include +#include +#include + +typedef struct { + char *TextFile; + char *AudioFile; + char *Voice; + char *Path; + float PreEmphasis; + float Speed; + AudioType Stype; + OUTTYPE FileType; + // nicht ganz so schön, aber funktioniert + PROOPT ProserOpt; +} tts_options; + +typedef struct { + NLPBLOCK GermanNlp; +} ttshandles; + +tts_options interface_get_cl_opts (int, char **); + +int interface_write_to_wav (char *, char *, ttshandles, tts_options); +ttshandles interface_init (tts_options); +long *interface_get_timing (ttshandles, char *); +#endif diff --git a/include/sentence.h b/include/sentence.h new file mode 100644 --- /dev/null +++ b/include/sentence.h @@ -0,0 +1,28 @@ +/** + * $Id: sentence.h 48 2008-01-09 23:59:19Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/sentence.h $ + */ + +#ifndef __SENTENCE_H__ +#define __SENTENCE_H__ + +#include +#include +#include +#include + +/* prepares the text */ +int *parse (char *); + +/* reads a textfile */ +char *readbuffer (char *); + +/* extracts a substring from a buffer */ +char *getSentence (char *, int, int); + +/* counts the words of a string */ +int words (char *); + +char **getstrings (char *, int *); + +#endif diff --git a/include/thread.h b/include/thread.h new file mode 100644 --- /dev/null +++ b/include/thread.h @@ -0,0 +1,37 @@ +/** + * $Id: thread.h 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/include/thread.h $ + */ + +#ifndef __THREAD_H__ +#define __THREAD_H__ + +#include +#include +#include +#include + +#ifndef TIMEOUT +#define TIMEOUT 186 +#endif + +typedef struct { + int satz; + int items; + char *fname; + tts_options ttsopt; + ttshandles ttsh; + char **sentences; + long **timings; +} ThreadData; + +/* reads the wavefiles from disk */ +void readtext (ThreadData *); + +/* stores the data on the harddisk */ +void writewav (ThreadData *); + +/* (re)starts a thread */ +int p_thread_restart (pthread_t, void *, ThreadData *); + +#endif diff --git a/player/Makefile b/player/Makefile new file mode 100644 --- /dev/null +++ b/player/Makefile @@ -0,0 +1,34 @@ + CC = gcc +CFLAGS = -Wall -O2 -DLINUX -DWINAPI="" $(shell sdl-config --cflags) +INCLUDE= -I../include +LDFLAGS = -Wl,-rpath,lib $(shell sdl-config --libs) + DEBUG = -g -ggdb +TARGET = ../bin/audioplayer +OBJECTS = \ + main.o \ + audioplayer.o + +.c.o: + @$(CC) -c $(CFLAGS) $(INCLUDE) $(DEBUG) -o $@ $< + @echo "CC $<" + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + @$(CC) $(OBJECTS) $(LDFLAGS) -o $@ + @echo "LD OBJECTS -o $@" + +.PHONY: clean distclean + +clean: + @rm -f *.o *~ + @echo "CLEANING $$PWD" + +distclean: + @make clean + @rm -f $(TARGET) + @echo "WIPING $$PWD OUT" + +install: $(TARGET) + install -m 755 $(TARGET) ~/bin/ + diff --git a/player/audioplayer.c b/player/audioplayer.c new file mode 100644 --- /dev/null +++ b/player/audioplayer.c @@ -0,0 +1,228 @@ +/** + * $Id: audioplayer.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/player/audioplayer.c $ + */ + +#include + +typedef struct { + SDL_AudioSpec spec; // internal structure + Uint8 *sound_buffer; // malloced pointer from sdl + Uint32 sound_pos; // position + Uint32 sound_len; // length in bytes + Uint32 length; // Length in Milliseconds + + int opened; // bool opened + int loaded; // bool loaded + int verbose; // verbose messages + int timeout; // timeout + int timer; // bool timer +} AudioDevice; + +AudioDevice AD; + +int audio_init () +{ + // initialize SDL for audio output + if (SDL_Init (SDL_INIT_AUDIO) < 0) { + fprintf (stderr, "audio_init: Cannot initialize SDL-Subsystem\n"); + return -1; + } + + AD.sound_buffer = 0; + AD.sound_pos = 0; + AD.sound_len = 0; + + AD.opened = 0; + AD.loaded = 0; + AD.verbose = 0; + + AD.timeout = 0; + AD.timer = 0; + + return 0; +} + +void audio_shutdown () +{ + // quit sdl + SDL_AudioQuit (); + SDL_Quit (); +} + +static void Callback (void *userdata, Uint8 * stream, int len) +{ + Uint8 *waveptr = NULL; + + /* + * loaded = 0 Segmentation fault + * len = 0 nothing to play + */ + if (!(AD.loaded && len)) + return; + + waveptr = AD.sound_buffer + AD.sound_pos; + SDL_MixAudio (stream, waveptr, len, SDL_MIX_MAXVOLUME); + AD.sound_pos += len; // bereits gespielt + + if (audioplayer_gettime () > (AD.length - AD.timeout)) { + AD.sound_pos = AD.length; + AD.sound_len = 0; + AD.timer = 0; + } +} + +Uint32 audioplayer_getsoundlen () +{ + return AD.sound_len; +} + +void audioplayer_media_info (char *fname) +{ + AD.length = audioplayer_getwavelength (fname); + + printf ("Playing %s: %d Hz, %d Bit Audio, %d Channel(s), %3.2d ms\n", + fname, AD.spec.freq, audioplayer_getbitrate (), AD.spec.channels, AD.length); +} + +int audioplayer_getbitrate () +{ + int bitrate = 0; + + switch (AD.spec.format) { + case AUDIO_U8: + case AUDIO_S8: + bitrate = 8; + break; + case AUDIO_S16LSB: + case AUDIO_S16MSB: + case AUDIO_U16LSB: + case AUDIO_U16MSB: + bitrate = 16; + break; + } + + return bitrate; +} + +Uint32 audioplayer_getwavelength (char *fname) +{ + struct stat info; + Uint32 value = 0; + + if (fname == NULL) + return AD.length; + + if (stat (fname, &info) == 0) + /* + * the value is really big, so be carefull + * changes in the order result in different values + * the storage type isn`t well choosen... + */ + value = ((info.st_size) / (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)) * 1000.0); + + return (value); +} + +Uint32 audioplayer_getposition () +{ + return AD.sound_pos; +} + +Uint32 audioplayer_gettime () +{ + Uint32 ret; + + ret = AD.sound_pos / (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)) * 1000.0; + + return ret; +} + +void audioplayer_settime (long tm) +{ + AD.sound_pos = tm / 1000.0 * (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)); + + /* + * Eine Adresse ... + */ + while ((AD.sound_pos % 2) != 0) + AD.sound_pos++; + + if (AD.verbose) + printf ("\e[31m[ATTENTION]\e[37m [#%d/#%d] = %d ms\n", AD.sound_pos, AD.sound_len, audioplayer_gettime ()); +} + +void audioplayer_stop () +{ + /* + * stops the callback function + */ + SDL_PauseAudio (1); + AD.spec.callback = NULL; + + if (AD.loaded == 1) + SDL_FreeWAV (AD.sound_buffer); + + AD.loaded = 0; + AD.timer = 0; +} + +void audioplayer_delay (Uint32 milliseconds) +{ + struct timespec tm; + + tm.tv_sec = 0; + tm.tv_nsec = milliseconds * 1000; + + while (AD.timer) + nanosleep (&tm, NULL); +} + +void audioplayer_setverbose (int verbose) +{ + AD.verbose = verbose; +} + +int audioplayer (char *fname, Uint32 timeout) +{ + if (SDL_LoadWAV (fname, &AD.spec, &AD.sound_buffer, &AD.sound_len) == NULL) { + printf ("SDL: %s\n", SDL_GetError ()); + return -1; + } + + AD.loaded = 1; + + if (!AD.opened) { + AD.spec.callback = Callback; + if (SDL_OpenAudio (&AD.spec, NULL) < 0) { + fprintf (stderr, "Cannot open audio device: %s\n", SDL_GetError ()); + return -1; + } + AD.opened = 1; + } + + /* + * calls implicitly getwavelength + */ + audioplayer_media_info (fname); + + AD.sound_pos = 0; + + /* + * starts playback via Callback function + */ + SDL_PauseAudio (0); + + /* + * initializes the timeout mechanism + */ + AD.timer = 1; + AD.timeout = timeout; + + /* + * controlled delay + */ + audioplayer_delay (timeout); + + return 0; +} diff --git a/player/main.c b/player/main.c new file mode 100644 --- /dev/null +++ b/player/main.c @@ -0,0 +1,50 @@ +/** + * $Id: main.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/player/main.c $ + */ + +#include +#include +#include + +#define TIMEOUT 186 + +void help (char *prgname) +{ + printf ("Usage: %s [-h] sndfile1,[sndfile2,...]\n", prgname); + printf ("\t sndfile\t: a valid wave file\n"); + printf ("\t-h help \t: shows this screen\n"); +} + +int main (int argc, char **argv) +{ + int i; + + while ((i = getopt (argc, argv, "h")) > 0) { + switch (i) { + case 'h': + help (argv[0]); + exit (0); + break; + default: + printf ("Unknown Option %c\n", i); + help (argv[0]); + exit (0); + } + } + + if (argc == optind) { + help (argv[0]); + exit (0); + } + + audio_init (); + + for (i = optind; i < argc; i++) { + audioplayer (argv[i], TIMEOUT); + audioplayer_stop (); + } + + audio_shutdown (); + return 0; +} diff --git a/run b/run new file mode 100755 --- /dev/null +++ b/run @@ -0,0 +1,68 @@ +#!/bin/bash +# $Header: /work/c/VirtualReader/run-20070320,v 1.3 2007/03/20 20:00:00 bordi $ + +VALGRIND="$(which valgrind)" +STRACE="$(which strace)" +GDB="$(which gdb)" +DDD="$(which ddd)" +SPLINT="$(which splint)" +BUILD="make -C src" +PROG="bin/vreader" +FILE="test.txt" +OPTIONS="-i ${FILE} -v voice/De_Carla_22kHz -p data -format WAV -o test.wav" + +$BUILD +if [ $? == 0 ] + then + echo + echo " VirtualReader - Version 0.1" + echo + echo " Navigation Keys:" + echo " ##############################" + echo " # i: next word #" + echo " # j: previous sentence #" + echo " # k: previous word #" + echo " # l: next sentence #" + echo " # p: print position #" + echo " # q: quit #" + echo " # r: reset to first position #" + echo " # s: print current sentence #" + echo " # v: switch verbosity on/off #" + echo " ##############################" + echo " Press ENTER to Start" + read + case $1 in + valgrind) + $VALGRIND $PROG $OPTIONS + ;; + strace) + $STRACE $PROG $OPTIONS + ;; + gdb) + echo "break 67">gdb.exec + echo "r $OPTIONS" >> gdb.exec + DEBUG=1 $GDB -x gdb.exec $PROG + rm -f gdb.exec + ;; + ddd) + echo "break 67">gdb.exec + echo "r $OPTIONS" >> gdb.exec + $DDD -x gdb.exec $PROG + rm -f gdb.exec + ;; + splint) + $SPLINT -Iinclude -DMBROLA -warnposix src/main.c + ;; + help) + echo "Usage: run [commands]" + echo "valid commands are: strace, gdb, \ +ddd, valgrind and help" + exit 0 + ;; + *) + $PROG $OPTIONS + ;; + esac +fi + +rm -f audio/*.wav; diff --git a/src/Makefile b/src/Makefile new file mode 100644 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,62 @@ +###### USER CONFIG ############### +TARGET = ../bin/vreader # +MBROLA = /opt/mbrola/bin/mbrola # +FESTIVAL= /usr/bin/festival # +INSTDIR = ~/bin # +##### USER CONFIG ENDS ########### + + CC = gcc +MCFLAGS = -Wall -O2 -DMBROLA $(shell sdl-config --cflags) +FCFLAGS = -Wall -O2 -DFESTIVAL $(shell sdl-config --cflags) +PCFLAGS = -Wall -O2 -DPROSER -DLINUX -DWINAPI="" $(shell sdl-config --cflags) +INCLUDE = -I../include +LDFLAGS = -Wl,-rpath,lib -L../lib $(shell sdl-config --libs) + DEBUG = -g -ggdb +OBJECTS = \ + main.o \ + sentence.o \ + keyboard.o \ + thread.o \ + audioplayer.o \ + proser_interface.o \ + mbrola_interface.o \ + festival_interface.o + +.c.o: + @if [ -f ../lib/libProserGerman.a ]; \ + then \ + $(CC) -c $(PCFLAGS) $(INCLUDE) $(DEBUG) -o $@ $<; \ + elif [ -x $(MBROLA) ]; \ + then \ + $(CC) -c $(MCFLAGS) $(INCLUDE) $(DEBUG) -o $@ $<; \ + elif [ -x $(FESTIVAL) ]; \ + then \ + $(CC) -c $(FCFLAGS) $(INCLUDE) $(DEBUG) -o $@ $<; \ + else \ + echo "Cannot find any TTS-System on your Machine"; \ + fi; + @echo "CC $<" + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + @if [ -f ../lib/libProserGerman.a ]; \ + then \ + $(CC) $(OBJECTS) $(LDFLAGS) -lProserGerman -o $@; \ + else \ + $(CC) $(OBJECTS) $(LDFLAGS) -o $@; fi + @echo "LD OBJECTS -o $@" + +.PHONY: clean distclean + +clean: + @rm -f *.o *~ + @echo "CLEANING $$PWD" + +distclean: + @make clean + @rm -f $(TARGET) + @echo "WIPING $$PWD OUT" + +install: $(TARGET) + install -m 755 $(TARGET) $(INSTDIR) diff --git a/src/audioplayer.c b/src/audioplayer.c new file mode 100644 --- /dev/null +++ b/src/audioplayer.c @@ -0,0 +1,232 @@ +/** + * $Id: audioplayer.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/audioplayer.c $ + */ + +#include + +typedef struct { + SDL_AudioSpec spec; // internal structure + Uint8 *sound_buffer; // malloced pointer from sdl + Uint32 sound_pos; // position + Uint32 sound_len; // length in bytes + Uint32 length; // Length in Milliseconds + + int opened; // bool opened + int loaded; // bool loaded + int verbose; // verbose messages + int timeout; // timeout + int timer; // bool timer +} AudioDevice; + +AudioDevice AD; + +int audio_init () +{ + /* + * initialize SDL for audio output + */ + if (SDL_Init (SDL_INIT_AUDIO) < 0) { + fprintf (stderr, "audio_init: Cannot initialize SDL-Subsystem\n"); + return -1; + } + + AD.sound_buffer = 0; + AD.sound_pos = 0; + AD.sound_len = 0; + + AD.opened = 0; + AD.loaded = 0; + AD.verbose = 0; + + AD.timeout = 0; + AD.timer = 0; + + return 0; +} + +void audio_shutdown () +{ + /* + * quit sdl + */ + SDL_AudioQuit (); + SDL_Quit (); +} + +static void Callback (void *userdata, Uint8 * stream, int len) +{ + Uint8 *waveptr = NULL; + + /* + * loaded = 0 Segmentation fault + * len = 0 nothing to play + */ + if (!(AD.loaded && len)) + return; + + waveptr = AD.sound_buffer + AD.sound_pos; + SDL_MixAudio (stream, waveptr, len, SDL_MIX_MAXVOLUME); + AD.sound_pos += len; /* bereits gespielt */ + + if (audioplayer_gettime () > (AD.length - AD.timeout)) { + AD.sound_pos = AD.length; + AD.sound_len = 0; + AD.timer = 0; + } +} + +Uint32 audioplayer_getsoundlen () +{ + return AD.sound_len; +} + +void audioplayer_media_info (char *fname) +{ + AD.length = audioplayer_getwavelength (fname); + + printf ("Playing %s: %d Hz, %d Bit Audio, %d Channel(s), %3.2d ms\n", + fname, AD.spec.freq, audioplayer_getbitrate (), AD.spec.channels, AD.length); +} + +int audioplayer_getbitrate () +{ + int bitrate = 0; + + switch (AD.spec.format) { + case AUDIO_U8: + case AUDIO_S8: + bitrate = 8; + break; + case AUDIO_S16LSB: + case AUDIO_S16MSB: + case AUDIO_U16LSB: + case AUDIO_U16MSB: + bitrate = 16; + break; + } + + return bitrate; +} + +Uint32 audioplayer_getwavelength (char *fname) +{ + struct stat info; + Uint32 value = 0; + + if (fname == NULL) + return AD.length; + + if (stat (fname, &info) == 0) + /* + * the value is really big, so be carefull + * changes in the order result in different values + * the storage type isn`t well choosen... + */ + value = ((info.st_size) / (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)) * 1000.0); + + return (value); +} + +Uint32 audioplayer_getposition () +{ + return AD.sound_pos; +} + +Uint32 audioplayer_gettime () +{ + Uint32 ret; + + ret = AD.sound_pos / (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)) * 1000.0; + + return ret; +} + +void audioplayer_settime (long tm) +{ + AD.sound_pos = tm / 1000.0 * (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)); + + /* + * Eine Adresse ... + */ + while ((AD.sound_pos % 2) != 0) + AD.sound_pos++; + + if (AD.verbose) + printf ("\e[31m[ATTENTION]\e[37m [#%d/#%d] = %d ms\n", AD.sound_pos, AD.sound_len, audioplayer_gettime ()); +} + +void audioplayer_stop () +{ + /* + * stops the callback function + */ + SDL_PauseAudio (1); + AD.spec.callback = NULL; + + if (AD.loaded == 1) + SDL_FreeWAV (AD.sound_buffer); + + AD.loaded = 0; + AD.timer = 0; +} + +void audioplayer_delay (Uint32 milliseconds) +{ + struct timespec tm; + + tm.tv_sec = 0; + tm.tv_nsec = milliseconds * 1000; + + while (AD.timer) + nanosleep (&tm, NULL); +} + +void audioplayer_setverbose (int verbose) +{ + AD.verbose = verbose; +} + +int audioplayer (char *fname, Uint32 timeout) +{ + if (SDL_LoadWAV (fname, &AD.spec, &AD.sound_buffer, &AD.sound_len) == NULL) { + printf ("SDL: %s\n", SDL_GetError ()); + return -1; + } + + AD.loaded = 1; + + if (!AD.opened) { + AD.spec.callback = Callback; + if (SDL_OpenAudio (&AD.spec, NULL) < 0) { + fprintf (stderr, "Cannot open audio device: %s\n", SDL_GetError ()); + return -1; + } + AD.opened = 1; + } + + /* + * calls implicitly getwavelength + */ + audioplayer_media_info (fname); + + AD.sound_pos = 0; + + /* + * starts playback via Callback function + */ + SDL_PauseAudio (0); + + /* + * initializes the timeout mechanism + */ + AD.timer = 1; + AD.timeout = timeout; + + /* + * controlled delay + */ + audioplayer_delay (timeout); + + return 0; +} diff --git a/src/festival_interface.c b/src/festival_interface.c new file mode 100644 --- /dev/null +++ b/src/festival_interface.c @@ -0,0 +1,75 @@ +/** + * $Id: festival_interface.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/festival_interface.c $ + */ + +#ifdef FESTIVAL +#include + +ttshandles interface_init (tts_options t_options) +{ + ttshandles handle; + + return handle; +} + +long *interface_get_timing (ttshandles tts, char *text) +{ + int i; + long *t = NULL; + + for (i = 0; i < strlen (text); i++) { + t = realloc (t, (i + 2) * sizeof (long)); + t[i] = i * 20 + 10; + } + + t[i] = 0; + return t; +}; + +int interface_write_to_wav (char *fname, char *text, ttshandles t_handles, tts_options t_options) +{ + char *s = NULL; + + if ((s = malloc (strlen (text) + 80)) == NULL) + return -1; + + sprintf (s, "printf \"%s\" | bin/festival_write_wav - %s", text, fname); +#ifdef DEBUG + printf ("Writing %s\n", fname); +#endif + system (s); + + if (s != NULL) + free (s); + return 0; +} + +tts_options interface_get_cl_opts (int num, char **arguments) +{ + tts_options options = { 0, 0, 0, 0 }; + int c; + + while ((c = getopt (num, arguments, "p:v:i:")) > 0) { + switch (c) { + case 'p': + options.Path = optarg; + break; + case 'v': + options.Voice = optarg; + break; + case 'i': + options.TextFile = optarg; + break; + } + } + + if (options.TextFile == NULL) { + fprintf (stderr, "It's substantial to give a text file by -i !\n"); + exit (0); + } + + return options; +} + +#endif diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100644 --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,36 @@ +/** + * $Id: keyboard.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/keyboard.c $ + */ + +#include + +char getSingleKey () +{ + // read and return single key with 1 second timeout + + struct termios new_settings; + struct termios stored_settings; + int timeout = 1; // 1 sec timeout + char c; + + tcgetattr (0, &stored_settings); + new_settings = stored_settings; + + new_settings.c_lflag &= ~(ICANON | ECHO); + + new_settings.c_cc[VTIME] = timeout * 10; + new_settings.c_cc[VMIN] = 0; + tcsetattr (0, TCSANOW, &new_settings); + + fflush (stdout); + c = getchar (); + + tcsetattr (0, TCSANOW, &stored_settings); + +#ifdef DEBUG + if (isalpha (c)) + printf ("received keypress: %c\n", c); +#endif + return c; +} diff --git a/src/main.c b/src/main.c new file mode 100644 --- /dev/null +++ b/src/main.c @@ -0,0 +1,223 @@ +/** + * $Id: main.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/main.c $ + */ + +#include +#include +#include +#include +#include + +#define STARTWRITER 0 +#define WRITER 1 +#define READER 2 + +#define STEP 2 // jump back n-steps + +void find_next (ThreadData * data) +{ + long tm = (long)audioplayer_gettime (); + long position; + int i, max; + + max = words (data->sentences[data->satz]); + + for (i = 0; i < max; i++) { + if (data->timings[data->satz][i] > tm) + break; + } + + position = data->timings[data->satz][i]; + audioplayer_settime (position); +} + +void find_prev (ThreadData * data) +{ + long tm = (long)audioplayer_gettime (); + long position; + int i, max; + + max = words (data->sentences[data->satz]); + + for (i = STEP; i < max; i++) { + if (data->timings[data->satz][i] > tm) + break; + } + + position = data->timings[data->satz][i - STEP]; + audioplayer_settime (position); +} + +int main (int argc, char **argv) +{ + char **sentences = NULL; // to store an array of strings + char *Text = NULL; // to store the content of a textfile + long **timings = NULL; // to store the timings + int i, count; // sentence counter + int verbose = 0; + + pthread_t p_thread, w_thread; + + ThreadData *data[3]; // independent thread data + + tts_options ttsopt = interface_get_cl_opts (argc, argv); + ttshandles ttsh = interface_init (ttsopt); + + if ((Text = readbuffer (ttsopt.TextFile)) != NULL) { + sentences = getstrings (Text, &count); + free (Text); + } else { + printf ("ERROR: Textfile %s is corrupt.\n", ttsopt.TextFile); + free (ttsopt.TextFile); + free (ttsopt.AudioFile); + free (ttsopt.Voice); + free (ttsopt.Path); + + return EXIT_SUCCESS; + } + + // Timinganalyse des Textes + if ((timings = calloc ((size_t) count, sizeof (long *) + 1)) == NULL) { + free (ttsopt.TextFile); + free (ttsopt.AudioFile); + free (ttsopt.Voice); + free (ttsopt.Path); + + return EXIT_SUCCESS; + } + + for (i = 0; i < count; i++) { + timings[i] = interface_get_timing (ttsh, sentences[i]); + } + + /* + * Initialize Sound + */ + if (audio_init ()) + fprintf (stderr, "AUDIO ERROR\n"); + + for (i = 0; i < 3; i++) { + data[i] = malloc (sizeof (ThreadData) + 1); + if (data[i] == NULL) + exit (0); + data[i]->ttsopt = ttsopt; + data[i]->ttsh = ttsh; + data[i]->fname = ttsopt.TextFile; + data[i]->sentences = sentences; + data[i]->timings = timings; + } + + /* + * write sentences from 0-1 :) + */ + data[STARTWRITER]->satz = 0; + data[STARTWRITER]->items = 1; + + pthread_create (&w_thread, NULL, (void *)writewav, data[STARTWRITER]); + pthread_join (w_thread, NULL); + + /* + * write sentences from 1-count :) + */ + data[WRITER]->satz = 1; + data[WRITER]->items = count; + pthread_create (&w_thread, NULL, (void *)writewav, data[WRITER]); + + data[READER]->satz = 0; + data[READER]->items = count; + pthread_create (&p_thread, NULL, (void *)readtext, data[READER]); + + while (data[READER]->satz < data[READER]->items) { + switch (getSingleKey ()) { + case 'i': + // next word + if (data[READER]->satz < data[READER]->items) + find_next (data[READER]); + else + printf ("ERROR: Cannot seek anymore\n"); + break; + + case 'j': + // previous sentence + if (data[READER]->satz > 0) { + data[READER]->satz--; + p_thread_restart (p_thread, readtext, data[READER]); + } + break; + + case 'k': + // previous word + find_prev (data[READER]); + break; + + case 'l': + // next sentence + if (data[READER]->satz < data[READER]->items) { + data[READER]->satz++; + p_thread_restart (p_thread, readtext, data[READER]); + } + break; + + case 'p': + // print useful informations + printf ("Current Position: [%5d/%5d]ms = #%d\n", + audioplayer_gettime (), audioplayer_getwavelength (NULL), audioplayer_getposition ()); + break; + + case 'q': + // quit + pthread_cancel (w_thread); + pthread_cancel (p_thread); + data[READER]->satz = data[READER]->items; + break; + + case 'r': + // reset player + pthread_cancel (p_thread); + if (!pthread_join (p_thread, NULL)) + printf ("RESET\n"); + break; + case 's': + printf ("S[%d]: %s\n", data[READER]->satz, sentences[data[READER]->satz]); + break; + case 'v': + verbose ^= 1; + audioplayer_setverbose (verbose); + break; + + default: + /* + * unknown command + */ + break; + } + } + + if (!pthread_join (p_thread, NULL)) + printf ("The last thread exited\n"); + + // cleanup memory + for (i = 0; i < count; i++) { + if (sentences[i] != NULL) + free (sentences[i]); + if (timings[i] != NULL) + free (timings[i]); + } + + if (sentences != NULL) + free (sentences); + + if (timings != NULL) + free (timings); + + for (i = 0; i < 3; i++) + if (data[i] != NULL) + free (data[i]); + + /* + * Cleanup sound + */ + audio_shutdown (); + pthread_exit (NULL); +} diff --git a/src/mbrola_interface.c b/src/mbrola_interface.c new file mode 100644 --- /dev/null +++ b/src/mbrola_interface.c @@ -0,0 +1,79 @@ +/** + * $Id: mbrola_interface.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/mbrola_interface.c $ + */ + +#ifdef MBROLA +#include + +ttshandles interface_init (tts_options t_options) +{ + ttshandles handle; + + return handle; +} + +long *interface_get_timing (ttshandles tts, char *text) +{ + long *t = NULL; + int i = 0; + + for (i = 0; i < strlen (text); i++) { + t = realloc (t, (i + 2) * sizeof (long)); + t[i] = i * 20 + 10; + } +#ifdef DEBUG + printf ("\nTiming analysis ends\n"); +#endif + + t[i] = 0; + + return t; +}; + +int interface_write_to_wav (char *fname, char *text, ttshandles t_handles, tts_options t_options) +{ + char *s = NULL; + + if ((s = malloc (strlen (text) + 80)) == NULL) + return -1; + + sprintf (s, "printf \"%s\" | bin/mbrola_write_wav - %s", text, fname); +#ifdef DEBUG + printf ("Writing %s\n", fname); +#endif + system (s); + + if (s != NULL) + free (s); + return 0; +} + +tts_options interface_get_cl_opts (int num, char **arguments) +{ + tts_options options = { 0, 0, 0, 0 }; + int c; + + while ((c = getopt (num, arguments, "p:v:i:")) > 0) { + switch (c) { + case 'p': + options.Path = optarg; + break; + case 'v': + options.Voice = optarg; + break; + case 'i': + options.TextFile = optarg; + break; + } + } + + if (options.TextFile == NULL) { + fprintf (stderr, "It's substantial to give a text file by -i !\n"); + exit (0); + } + + return options; +} + +#endif diff --git a/src/proser_interface.c b/src/proser_interface.c new file mode 100644 --- /dev/null +++ b/src/proser_interface.c @@ -0,0 +1,167 @@ +/** + * $Id: proser_interface.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/proser_interface.c $ + */ + +#ifdef PROSER +#include + +ttshandles interface_init (tts_options propt) +{ + + ttshandles pr_handle; + + // initialize Proser options and set default values for a given voice + if (init_ProserOpt (&propt.ProserOpt, propt.Voice)) { + fprintf (stderr, "Fatal error occurred in set_ProserOpt: %s\n", GetLastNlpErrorDescription (NULL)); + exit (-1); + } + // the path to the TTS input data like dictionaries and prosodic stuff has to be set + + propt.ProserOpt.Path = strdup (propt.Path); + + // here some more manipulations of the standard settings can be done + + propt.ProserOpt.PreEmphasis = propt.PreEmphasis; + propt.ProserOpt.Speed = propt.Speed; + + // initialize channel // + if (!(pr_handle.GermanNlp = OpenNlp (&propt.ProserOpt))) { + fprintf (stderr, "Fatal error occurred in OpenNlp: %s (%d)\n", + GetLastNlpErrorDescription (pr_handle.GermanNlp), (int)GetLastNlpError (pr_handle.GermanNlp)); + exit (-1); + } + // delete structure for Proser Options // + if (delete_ProserOpt (&propt.ProserOpt)) { + fprintf (stderr, "Fatal error occurred in delete_ProserOpt: %s\n", GetLastNlpErrorDescription (NULL)); + exit (-1); + } + return pr_handle; +} + +int interface_write_to_wav (char *Filename, char *Text, ttshandles prosh, tts_options propt) +{ + // write Text to a file + if (ProserSynthText (prosh.GermanNlp, Text, Filename, propt.FileType, propt.Stype)) { + fprintf (stderr, "Fatal error occurred in ProserSynthText: %s\n", GetLastNlpErrorDescription (prosh.GermanNlp)); + return (-1); + } + return (0); +} + +long *interface_get_timing (ttshandles prosh, char *Text) +{ + // analyze given text, return array with long values containing + // time in ms when each word of the given text will start in the + // output + long *timings = NULL; + LPPHONEME PhoneticSentenceDescription; + long NumberOfPhoneticUnits; + int i = 0, WordNum = 0; + + PhoneticSentenceDescription = TextToPho (prosh.GermanNlp, Text, &NumberOfPhoneticUnits); +#ifdef DEBUG + printf ("\n[S] %s\n", Text); +#endif + while (i < (NumberOfPhoneticUnits - 1)) { + if (PhoneticSentenceDescription[i + 1].Event != 0) { + timings = realloc (timings, (WordNum + 2) * sizeof (long)); + timings[WordNum] = PhoneticSentenceDescription[i + 1].Offset; +#ifdef DEBUG + printf ("Wort:\t %i \t Time: %ld ms\n", WordNum, PhoneticSentenceDescription[i + 1].Offset); +#endif + WordNum++; + } + i++; + } + timings[WordNum] = 0; + return timings; +} + +tts_options interface_get_cl_opts (int argc, char **argv) +{ + int i, j; + tts_options propt; + + propt.TextFile = propt.AudioFile = propt.Voice = propt.Path = NULL; + + j = 0; + propt.PreEmphasis = 0.0f; + propt.Speed = 1.0f; + propt.Stype = LIN16; + propt.FileType = WAV; + + for (i = 1; i < argc; i++) { + if (!strcmp (argv[i], "-i")) { + propt.TextFile = argv[++i]; + continue; + } else if (!strcmp (argv[i], "-o")) { + propt.AudioFile = argv[++i]; + continue; + } else if (!strcmp (argv[i], "-v")) { + propt.Voice = argv[++i]; + continue; + } else if (!strcmp (argv[i], "-p")) { + propt.Path = argv[++i]; + continue; + } else if (!strcmp (argv[i], "-pre")) { + propt.PreEmphasis = atof (argv[++i]); + continue; + } else if (!strcmp (argv[i], "-speed")) { + propt.Speed = atof (argv[++i]); + continue; + } else if (!strcmp (argv[i], "-format")) { + i++; + for (j = 0; j < (int)strlen (argv[i]); j++) + argv[i][j] = toupper (argv[i][j]); + if (!strcmp (argv[i], "RAW")) { + propt.FileType = RAW; + continue; + } else if (!strcmp (argv[i], "WAV")) { + propt.FileType = WAV; + continue; + } else if (!strcmp (argv[i], "AU")) { + propt.FileType = AU; + continue; + } else { + fprintf (stderr, "Unknown output type %s\n", argv[i]); + fprintf (stderr, "Supported output types: RAW - AU - WAV\n"); + exit (-1); + } + } + if (strcmp (argv[i], "-audio") == 0) { + i++; + for (j = 0; j < strlen (argv[i]); j++) + argv[i][j] = toupper (argv[i][j]); + if (!strcmp (argv[i], "LIN16")) { + propt.Stype = LIN16; + continue; + } else if (!strcmp (argv[i], "LIN8")) { + propt.Stype = LIN8; + continue; + } else if (!strcmp (argv[i], "ULAW")) { + propt.Stype = ULAW; + continue; + } else if (!strcmp (argv[i], "ALAW")) { + propt.Stype = ALAW; + continue; + } + } else { + fprintf (stderr, "Unknown option: %s\n", argv[i]); + exit (-1); + } + } + + if (!propt.TextFile) { + fprintf (stderr, "It's substantial to give a text file by -i !\n"); + exit (-1); + } else if (!propt.Voice) { + fprintf (stderr, "It's substantial to give a voice database by -v !\n"); + exit (-1); + } else if (!propt.Path) { + fprintf (stderr, "It's substantial to give a path for the prosodic stuff by -p !\n"); + exit (-1); + } + return (propt); +} +#endif diff --git a/src/sentence.c b/src/sentence.c new file mode 100644 --- /dev/null +++ b/src/sentence.c @@ -0,0 +1,164 @@ +/** + * $Id: sentence.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/sentence.c $ + */ + +#include + +int words (char *ptr) +{ + char *buffer = NULL; + char *token; + int count = 0; + + if (ptr != NULL) + buffer = strdup (ptr); + + if (buffer == NULL) + return -1; + + token = strtok (buffer, " "); + while (token != NULL) { + token = strtok (NULL, " "); + count++; + } + + if (buffer != NULL) + free (buffer); + + return count; +} + +int *parse (char *buf) +{ + int count = 0; + int i; + int *s_ptr = NULL; + + s_ptr = malloc (2 * sizeof (int)); + s_ptr[count++] = 0; + + for (i = 0; i < strlen (buf); i++) + switch (buf[i]) { + case '\n': + case '\t': + case '"': + case '\'': + buf[i] = ' '; + break; + case '.': + case '!': + case '?': + s_ptr = realloc (s_ptr, (count + 2) * sizeof (int)); + if (s_ptr == NULL) { + printf ("Not enough memory\n"); + return NULL; + } + s_ptr[count++] = ++i; + break; + default: + break; + } + + s_ptr[count] = 0; + + return s_ptr; +} + +char *readbuffer (char *fname) +{ + FILE *f; + char *text = NULL; + char buffer[201]; + + if ((f = fopen (fname, "r")) == NULL) { + fprintf (stderr, "ERROR: Cannot open %s for reading!\n", fname); + return NULL; + } + + text = malloc (2); + *text = 0; + + while (fgets (buffer, 200, f) != 0) { + if ((text = realloc (text, strlen (text) + strlen (buffer) + 1)) == NULL) { + printf ("Not enough memory\n"); + return NULL; + }; + strcat (text, buffer); + *buffer = 0; + } + + fclose (f); + return text; +} + +char *getSentence (char *text, int start, int end) +{ + int i; + char *value = NULL; + + if ((i = end - start) < 0) + return NULL; + + value = malloc (i + 1); + if (value == NULL) + return NULL; + + i = 1; + while (i) { + switch (text[start]) { + case '.': + case ',': + case '!': + case '?': + case '(': + case ')': + case '\n': + case ' ': + start++; + break; + default: + i = 0; + break; + } + } + + for (i = start; i < end; i++) { + value[i - start] = text[i]; + } + + value[i - start] = 0; + + return value; +} + +char **getstrings (char *Text, int *value) +{ + char *ptr; + int i = 0; + int count = 0; + char **sentences = NULL; + int *s_ptr; + + if ((s_ptr = parse (Text)) == NULL) + return NULL; + + while (s_ptr[i + 1] > 0) { + if ((ptr = getSentence (Text, s_ptr[i], s_ptr[i + 1])) == NULL) + return NULL; + if (strlen (ptr) > 0) { + if ((sentences = realloc (sentences, (count + 1) * sizeof (char *) + 1)) == NULL) + return NULL; + sentences[count++] = strdup (ptr); + } + if (ptr != NULL) + free (ptr); + i++; + } + + if (s_ptr) + free (s_ptr); + + *value = count; + return sentences; +} diff --git a/src/thread.c b/src/thread.c new file mode 100644 --- /dev/null +++ b/src/thread.c @@ -0,0 +1,60 @@ +/** + * $Id: thread.c 53 2008-01-10 00:19:41Z mbroeker $ + * $URL: http://localhost/svn/c/VirtualReader/trunk/src/thread.c $ + */ + +#include + +int p_thread_restart (pthread_t p_thread, void *func, ThreadData * data) +{ + // stops a thread, waits for it to exit and then restarts it using + // the current data structure + int ret; + + pthread_cancel (p_thread); + if (pthread_join (p_thread, NULL)) + printf ("RESTART: Cannot find a suitable thread :)\n"); + + ret = pthread_create (&p_thread, NULL, (void *)func, data); + + /* + * TODO: Error Handling + */ + return ret; +} + +void readtext (ThreadData * data) +{ + // read the current sentence using the audio player + // TODO - test for existence (completeness?) of file before read + char fname[80]; + + pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + pthread_cleanup_push (audioplayer_stop, NULL); + + for (; data->satz < data->items; data->satz++) { + sprintf (fname, "audio/%s-%i.wav", data->fname, data->satz); + if ((audioplayer (fname, TIMEOUT)) != -1) + audioplayer_stop (); + else + pthread_exit (NULL); + } + + pthread_exit (NULL); + pthread_cleanup_pop (1); +} + +void writewav (ThreadData * data) +{ + // write wav file for each sentence between data->satz adn + // data->items + char fname[80]; + int i; + + for (i = data->satz; i < data->items; i++) { + sprintf (fname, "audio/%s-%i.wav", data->fname, i); + interface_write_to_wav (fname, data->sentences[i], data->ttsh, data->ttsopt); + } + + pthread_exit (NULL); +} diff --git a/test.txt b/test.txt new file mode 100644 --- /dev/null +++ b/test.txt @@ -0,0 +1,111 @@ +Einst herrschte ein großer starker Pirat über die sieben Weltmeere. +Überall war "er verhasst und bekannt. Er riss Alles an sich, was nicht +Niet- und Nagelfest war und erschreckte Alle durch seine angsteinflößede +Art und durch sein Aussehen. Seine Besatzung hatte sich nach und nach +aus dem Staube" gemacht, wei der Pirat Alles für sich haben wollte und +ungerecht zu ihnen war. Er machte ihnen das Leben an Bord stets zur +Hölle. Das Schiff des Piratens wurde immer prunkvoller und schöner. Die +Segel waren aus goldener Seide, die Masten aus Elfenbein, das Holz aus +Teak, die Schrauben und Muttern, die alles fest hielten aus feinem +Silber. Das Glas in den Fenstern war aus schillerndem Perlmutt und +glitzerte mit dem Meer um die Wette. Zufrieden spazierte der Pirat über +sein Schiff, welches ein einzig großer Schatz war. Er liebte seinen +Reichtum und wollte immer mehr davon. BEkam er das, was er wollte, war +er ein paar Tage glücklich, doch dann wurde der Drang nach etwas noch +Größerem und noch Schönerem immer größer. Irgendetwas fehlte da, was ihn +unendlich glücklich machen würde. Doch kein Rubin der Welt, keine +goldene Krone, kein silberner Taler schaffte es ihn länger als 3 Tage +glücklich zu machen. Somit musste er, um seine Zufriedenheit zu erhalten +sehr oft auf Raubzug gehen. +Eines Tages kam er an eine kleine Insel. Schon von weitem sah er die +kleine Truppe von Eingeborenen, die um ein Lagerfeuer saßen, trommelten, +aßen und lachten. +Sie hatten nichts bei sich, außer ihre Baströckchen, ihre Instrumente +und ihr spärliches Mahl – +und doch machten sie einen glücklicheren Eindruck, als er sich je +vorstellen konnte. +Er hatte selten Menschen gesehen, die so glücklich und zufrieden waren +wie diese. +Er fragte sich, welche Schätze sie wohl zu Hause versteckt hielten. Denn +ohne einen großen Reichtum ließe sich in seinem Piratenkopf die große +Glückseeligkeit nicht erklären. +Als er den Anker warf und an Land ging, stürmte der Stamm der +Eingeborenen hastig von Dannen, bis auf ein kleiner Junge. Der Pirat +fragte den Jungen, warum sie anderen weggelaufen sind und was es denn +bei ihm im Dorf schönes zu holen gäbe. +Der kleine Junge antwortete munter und fürchtete sich kein bisschen vor +dem Pirat, auch wenn dieser furcht erregend aussah. In seinen Augen +nämlich sah der Junge Unsicherheit, Traurigkeit und Einsamkeit. Außerdem +bemerkte der Junge das Holzbein des Piraten. Damit hätte er ihm nie so +flink folgen können. Der Junge erzählte dem Pirat von seinem Dorf, den +Blätter und Strohhütten und von seiner Familien und seinen Freunden. +„Freunde?“ fragte der Pirat. Er konnte sich nichts darunter vorstellen +unter diesem Begriff. +Der kleine Junge versuchte ihm zu erklären, was Freundschaft ist und der +Pirat staunte nicht schlecht. Nun wollte er unbedingt einen Freund +haben, denn der Junge meinte, ein Freund sei der größte Schatz der Welt. +Das machte den Piraten neugierig. +Doch so einfach war das nicht. Ein Freund ist unbezahlbar und das +verstand der Pirat nicht. +Man konnte einen Freund nicht einfach kaufen oder stibitzen. +Andere Dinge zählten. Dinge, von denen der Pirat noch nie in seinem +Leben gehört hatte. +Der kleine Junge wollte dem Pirat zeigen, was Freundschaft ist und sie +trafen sich jeden Tag, redeten, lachten und machten Feuer. Der Junge +zeigte dem Pirat wie man Fische fängt und der Pirat zeigte dem Jungen, +in welchen Muscheln man dir größten Perlen findet. +Nach und nach verloren auch die anderen vom Stamm die Furcht vor dem +Piraten und abends wurde am Lagerfeuer getanzt, gesungen und gelacht. +Der Pirat fühlte sich wohler als je zuvor und hatte durch den kleinen +Jungen erfahren, was es bedeutet, Freunde zu finden. +Er wurde von Tag zu Tag glücklicher und zufriedener und ihm stand wenig +Sinn nach seinen Räuberzügen. +Eines Tages jedoch wurde der kleine Junge sehr krank. Keine Heilpflanze +konnte ihm helfen, kein Schamane konnte Wunder bewirken, keiner der im +Tanz und Klang herbeigerufenen Geister halfen ihm. Ein wichtiges +Medikament von einem anderen Kontinent wurde benötigt. +Somit machte sich der Pirat sofort auf die Reise, dieses Medikament zu +besorgen. Die Zeit war knapp. Es blieben dem Jungen nur noch wenige +Wochen, vielleicht sogar nur Tage. +Der Stamm war dem Piraten sehr dankbar und sie beteten und hofften auf +eine baldige Rückkehr mit dem Medikament. Der Pirat fuhr drei Tage und +drei Nächte durch Wind und Wetter und kam endlich an seinem Zielort an. +Er machte sich große Sorgen um seinen Freund. Das Medikament war sehr +teuer. Der Pirat wollte es mit Kanonenschuss und Messerwurf erwerben, +doch irgendetwas hielt ihn zurück. Er konnte seine Boshaftigkeit nicht +mehr zeigen, denn wenn er in die Gesichter von den Menschen schaute, +ihre Blicke sah, dann musste er an seinen Freund denken und an seinen +Stamm, der ihn so freundlich aufgenommen hatte. +Also beschloss er das Medikament zu kaufen. Er gab sehr viel von seinem +Reichtum ab und verkaufte ebenso einen Teil davon, um ein paar Geschenke +und Nahrungsmittel für den Stamm mitzubringen. Nun war sein Schiff gar +nicht mehr so pompös, wie zuvor, aber es störte ihn seltsamerweise nicht +weiter. Für ihn zählte nur das Leben seines Freundes, dem kleinen +Jungen. Auf dem Weg zu ihm kam er an einer kleinen einsamen Insel +vorbei, auf der eine Familie gestrandet war. Sie flehten um Hilfe, dass +er sie mitnehme. Da der Pirat jedoch schnellst möglich in eine andere +Richtung musste, um zur Insel seines Freundes zu gelangen, blieb ihm +nichts anderes übrig als ihnen das große Schiff zu überlassen. Er selbst +fuhr mit dem kleinen Beiboot zur Insel, die nicht mehr so weit entfernt +war. +Ein wenig seltsam war es schon, alles abgegeben zu haben, doch mit dem +Seufzer kam auch eine seltsame Erleichterung. Wie eine tonnenschwere +Last, die abgeworfen wurde. +Als der Pirat an der Insel ankam, wurde er schon erwartet. Der Junge war +noch schlechter zurecht als zuvor und der Guru hatte schon das letzte +Gebet ausgesprochen und ihn einbalsamiert. Das Medikament jedoch half +ihm in allerletzter Sekunde und er wurde von Tag zu Tag gesünder. Nach +einer Woche konnte er wieder aufstehen und mit den anderen Kindern +spielen. Während sein Vater einen Arm um den Pirat legte und „Mnumbai, y +zmuni“ – Danke, mein Freund, sagte, schaute der Pirat glücklich zu. Der +Junge hatte sein Leben zurückbekommen, Dank ihm. Und er hatte wahre +Freunde gefunden. Das hatte er dem Jungen zu verdanken. Der Pirat hatte +ebenfalls bewiesen, dass ihm ein Freund mehr wert ist, als alle Schätze +der Welt. Er wurde damit belohnt, dass er nicht weiter einsam war. +Der Stamm nahm den Pirat für immer bei sich auf und er war glücklich und +zufrieden bis an sein Lebensende. Sein kleiner Freund wuchs heran und +wurde irgendwann ein weiser Häuptling. Er erzählte seinen Kindern und +Kindeskindern am Lagerfeuer von seinem treuen Piratenfreund. Immer, wenn +er ein Piratenschiff am Horizont vorbeifahren sah, dachte er an seinen +Freund zurück, dem er sein Leben zu verdanken hatte. +