speechd-discuss
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 3/6] move src/cl to src/api/cl


From: william hubbs
Subject: [PATCH 3/6] move src/cl to src/api/cl
Date: Wed, 15 Sep 2010 15:55:05 -0500

From: William Hubbs <address@hidden>
To: address@hidden

---
 src/Makefile.am               |    3 +-
 src/api/Makefile.am           |    2 +
 src/api/cl/ChangeLog          |    8 +
 src/api/cl/README             |   16 +
 src/api/cl/configuration.lisp |   64 +++++
 src/api/cl/elisp.lisp         |   61 ++++
 src/api/cl/package.lisp       |   49 ++++
 src/api/cl/ssip.asd           |   30 ++
 src/api/cl/ssip.lisp          |  613 +++++++++++++++++++++++++++++++++++++++++
 src/api/cl/sysdep.lisp        |   54 ++++
 src/cl/ChangeLog              |    8 -
 src/cl/README                 |   16 -
 src/cl/configuration.lisp     |   64 -----
 src/cl/elisp.lisp             |   61 ----
 src/cl/package.lisp           |   49 ----
 src/cl/ssip.asd               |   30 --
 src/cl/ssip.lisp              |  613 -----------------------------------------
 src/cl/sysdep.lisp            |   54 ----
 18 files changed, 898 insertions(+), 897 deletions(-)
 create mode 100644 src/api/cl/ChangeLog
 create mode 100644 src/api/cl/README
 create mode 100644 src/api/cl/configuration.lisp
 create mode 100644 src/api/cl/elisp.lisp
 create mode 100644 src/api/cl/package.lisp
 create mode 100644 src/api/cl/ssip.asd
 create mode 100644 src/api/cl/ssip.lisp
 create mode 100644 src/api/cl/sysdep.lisp
 delete mode 100644 src/cl/ChangeLog
 delete mode 100644 src/cl/README
 delete mode 100644 src/cl/configuration.lisp
 delete mode 100644 src/cl/elisp.lisp
 delete mode 100644 src/cl/package.lisp
 delete mode 100644 src/cl/ssip.asd
 delete mode 100644 src/cl/ssip.lisp
 delete mode 100644 src/cl/sysdep.lisp

diff --git a/src/Makefile.am b/src/Makefile.am
index ea7168d..255f96c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -5,8 +5,7 @@
 # in SUBDIRS if they contain another Makefile)
 # otherwise they won't be included in distribution packages
 
-EXTRA_DIST = cl/ChangeLog cl/README cl/configuration.lisp cl/elisp.lisp \
-             cl/package.lisp cl/ssip.lisp cl/sysdep.lisp cl/ssip.asd \
+EXTRA_DIST = \
              guile/ChangeLog guile/Makefile guile/README guile/gssip.scm.in \
              guile/gssip.c guile/gssip.h
 
diff --git a/src/api/Makefile.am b/src/api/Makefile.am
index 9bee85e..9653288 100644
--- a/src/api/Makefile.am
+++ b/src/api/Makefile.am
@@ -2,3 +2,5 @@
 
 SUBDIRS= c
 
+EXTRA_DIST = cl
+
diff --git a/src/api/cl/ChangeLog b/src/api/cl/ChangeLog
new file mode 100644
index 0000000..1139b83
--- /dev/null
+++ b/src/api/cl/ChangeLog
@@ -0,0 +1,8 @@
+2004-01-20  Milan Zamazal  <pdm at brailcom.org>
+
+       * sysdep.lisp (open-network-stream): Socket creation for SBCL
+       fixed.
+
+       * ssip.lisp: Exportable symbolic parameter values converted to
+       keywords.
+
diff --git a/src/api/cl/README b/src/api/cl/README
new file mode 100644
index 0000000..ea73f4c
--- /dev/null
+++ b/src/api/cl/README
@@ -0,0 +1,16 @@
+This is a simple Common Lisp interface to Speech Dispatcher through the Speech
+Synthesis Interface Protocol (SSIP).
+
+Currently, there is no documentation, read the sources.
+The simplest use can be as follows:
+
+  (asdf:operate-on-system 'asdf:load-op :ssip)
+  (ssip:say-text "Hello, world!")
+
+It works with CLisp and SBCL, but it should be easy to port to other systems,
+just add the support to sysdep.lisp.
+
+Feel free to contact us with any questions regarding the Common Lisp interface
+at the Speech Dispatcher mailing list <speechd at lists.freebsoft.org>.
+
+-- Milan Zamazal <pdm at freebsoft.org>
diff --git a/src/api/cl/configuration.lisp b/src/api/cl/configuration.lisp
new file mode 100644
index 0000000..49a43f8
--- /dev/null
+++ b/src/api/cl/configuration.lisp
@@ -0,0 +1,64 @@
+;;; Configuration variables
+
+;; Author: Milan Zamazal <pdm at brailcom.org>
+
+;; Copyright (C) 2004 Brailcom, o.p.s.
+
+;; COPYRIGHT NOTICE
+
+;; 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 2 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+;; for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+
+
+(in-package :ssip)
+
+
+(defvar *host* "localhost"
+  "Name of the default host running speechd to connect to.")
+
+(defvar *port* (or (ignore-errors
+                     (car (read-from-string (getenv "SPEECHD_PORT"))))
+                   6560)
+  "Default port of speechd.")
+
+
+(defvar *default-text-priority* :text
+  "Default Speech Dispatcher priority of sent texts.")
+
+(defvar *default-sound-priority* :message
+  "Default Speech Dispatcher priority of sent sound icons.")
+
+(defvar *default-char-priority* :notification
+  "Default Speech Dispatcher priority of sent single letters.")
+
+
+(defvar *connection-parameters* '()
+  "Alist of connection names and their parameters.
+
+Each element of the list is of the form (CONNECTION-NAME . PARAMETERS), where
+CONNECTION-NAME is a connection name as expected to be in `speechd-client-name'
+and PARAMETERS is a property list with the pairs of parameter identifiers and
+parameter values.  Valid parameter names are the following symbols:
+language, message-priority, punctuation-mode, capital-character-mode, voice,
+rate, pitch, output-module.  See the corresponding speechd-set-* functions for
+valid parameter values.
+
+If the symbol t is specified as the connection name, the element defines
+default connection parameters if no connection specification applies.  Only one
+such an element is allowed in the whole alist.
+
+The message-priority parameter has a special meaning: It overrides priority of
+all messages sent through the connection.
+
+You must reopen the connections to apply the changes to this variable.")
diff --git a/src/api/cl/elisp.lisp b/src/api/cl/elisp.lisp
new file mode 100644
index 0000000..cf1cc2c
--- /dev/null
+++ b/src/api/cl/elisp.lisp
@@ -0,0 +1,61 @@
+;;; Elisp compatibility functions
+
+;; Author: Milan Zamazal <pdm at brailcom.org>
+
+;; Copyright (C) 2004 Brailcom, o.p.s.
+
+;; COPYRIGHT NOTICE
+
+;; 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 2 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+;; for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+
+
+(in-package :ssip)
+
+
+(defmacro while (condition &body body)
+  `(loop while ,condition
+         do (progn , at body)))
+
+
+(defun plist-get-internal (plist prop)
+  (cond
+    ((null plist)
+     nil)
+    ((eq (car plist) prop)
+     (cdr plist))
+    (t
+     (plist-get-internal (nthcdr 2 plist) prop))))
+  
+(defun plist-get (plist prop)
+  (first (plist-get-internal plist prop)))
+
+(defun plist-member (plist prop)
+  (not (null (plist-get-internal plist prop))))
+
+(defun plist-put (plist prop val)
+  (let ((value (plist-get-internal plist prop)))
+    (if value
+        (progn
+          (rplaca value val)
+          plist)
+        (list* prop val plist))))
+
+
+(defun user-login-name ()
+  (or (getenv "LOGNAME") (getenv "USER")))
+
+
+(defun concat (&rest args)
+  (apply #'concatenate 'string args))
diff --git a/src/api/cl/package.lisp b/src/api/cl/package.lisp
new file mode 100644
index 0000000..9a1be15
--- /dev/null
+++ b/src/api/cl/package.lisp
@@ -0,0 +1,49 @@
+;;; Package definition
+
+;; Author: Milan Zamazal <pdm at brailcom.org>
+
+;; Copyright (C) 2004 Brailcom, o.p.s.
+
+;; COPYRIGHT NOTICE
+
+;; 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 2 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+;; for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+
+
+(in-package :cl-user)
+
+
+(defpackage :ssip
+  (:use :cl)
+  (:export
+   ;; configuration.lisp
+   #:*application-name*
+   #:*client-name*
+   #:*language*
+   #:*spell*
+   ;; ssip.lisp
+   #:connection-names
+   #:set-language
+   #:open-connection
+   #:close-connection
+   #:reopen-connection
+   #:say-text
+   #:say-sound
+   #:say-char
+   #:cancel
+   #:stop
+   #:pause
+   #:resume
+   ))
+
diff --git a/src/api/cl/ssip.asd b/src/api/cl/ssip.asd
new file mode 100644
index 0000000..690fed0
--- /dev/null
+++ b/src/api/cl/ssip.asd
@@ -0,0 +1,30 @@
+;; Copyright (C) 2004 Brailcom, o.p.s.
+
+;; COPYRIGHT NOTICE
+
+;; 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 2 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+;; for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+
+
+(in-package :asdf)
+
+
+(defsystem :ssip :depends-on (:regex #+SBCL :sb-bsd-sockets)
+  :components
+  ((:file "package")
+   (:file "sysdep")
+   (:file "elisp")
+   (:file "configuration")
+   (:file "ssip"))
+  :serial t)
diff --git a/src/api/cl/ssip.lisp b/src/api/cl/ssip.lisp
new file mode 100644
index 0000000..3a2cddf
--- /dev/null
+++ b/src/api/cl/ssip.lisp
@@ -0,0 +1,613 @@
+;;; Speech Synthesis Interface Protocol (SSIP) interface
+
+;; Author: Milan Zamazal <pdm at brailcom.org>
+
+;; Copyright (C) 2004 Brailcom, o.p.s.
+
+;; COPYRIGHT NOTICE
+
+;; 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 2 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+;; for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+
+
+;;; Note: This library was ported from the Elisp library, so don't wonder much
+;;; about elispisms found here...
+
+
+(in-package :ssip)
+
+
+;;; Exported variables
+
+
+(defvar *application-name* "lisp"
+  "String defining current application name.")
+  
+(defvar *client-name* "default"
+  "String defining current client name.
+This variable's value defines which connection is used when communicating via
+SSIP, each connection has its own client name.  Usually, you select the proper
+client (connection) by assigning a value to this variable locally through
+`let'.")
+
+(defvar *language* nil
+  "If non-nil, it is an RFC 1766 language code, as a string.
+If text is read and this variable is non-nil, the text is read in the given
+language.")
+
+(defvar *spell* nil
+  "If non-nil, any spoken text is spelled.")
+
+
+;;; Internal constants and configuration variables
+
+
+(defparameter +version+ "$Id: ssip.lisp,v 1.3 2006-02-17 13:18:55 pdm Exp $"
+  "Version stamp of the source file.
+Useful only for diagnosing problems.")
+
+(defvar *language-codes*
+  '(("czech" . "cs")
+    ("english" . "en")
+    ("french" . "fr")
+    ("german" . "de"))
+  "Mapping of LANG values to language ISO codes.")
+
+(defvar *default-voice* "male1")
+(defvar *default-language* (or (cdr (assoc (getenv "LANG") *language-codes*
+                                           :test #'string=))
+                               "en"))
+
+(defparameter +parameter-names+
+  '((client-name . "CLIENT_NAME")
+    (language . "LANGUAGE")
+    (message-priority . "PRIORITY")
+    (punctuation-mode . "PUNCTUATION")
+    (pause-context . "PAUSE_CONTEXT")
+    (capital-character-mode . "CAP_LET_RECOGN")
+    (voice . "VOICE")
+    (rate . "RATE")
+    (pitch . "PITCH")
+    (spelling-mode . "SPELLING")
+    (output-module . "OUTPUT_MODULE")
+    ))
+
+(defparameter +list-parameter-names+
+  '((voices . "VOICES")))
+
+(defparameter +parameter-value-mappings+
+  '((message-priority
+     (:important .    "IMPORTANT")
+     (:message .      "MESSAGE")
+     (:text .         "TEXT")
+     (:notification . "NOTIFICATION")
+     (:progress .     "PROGRESS")
+     )
+    (punctuation-mode
+     (:none . "none")
+     (:some . "some")
+     (:all .  "all"))
+    (capital-character-mode
+     (:none . "none")
+     (:spell . "spell")
+     (:icon . "icon"))
+    (spelling-mode
+     (t . "on")
+     (nil . "off"))))
+
+(defparameter +volatile-parameters+ '(output-module))
+
+(defparameter +punctuation-modes+ '(("none" . none)
+                                    ("some" . some)
+                                    ("all" .  all)))
+
+(defparameter +capital-character-modes+ '(("none" .  none)
+                                          ("spell" . spell)
+                                          ("icon" .  icon)))
+
+
+;;; Internal variables
+
+
+(defstruct connection
+  name
+  host
+  port
+  (failure-p nil)
+  stream
+  (paused-p nil)
+  (in-block nil)
+  (transaction-state nil)
+  (parameters ())
+  (forced-priority nil)
+  (last-command nil))
+
+(defstruct request
+  string
+  (transaction-state '(nil nil)))
+
+(defvar *connections* (make-hash-table :test #'equal)
+  "Hash table mapping client names to `connection' instances.")
+
+(defvar *connection* nil
+  "Current connection.")
+
+
+
+;;; Utilities
+
+
+(defmacro iterate-clients (&rest body)
+  `(maphash #'(lambda (*client-name* _) (declare (ignore _)) , at body)
+    *connections*))
+
+(defmacro iterate-connections (&rest body)
+  `(maphash #'(lambda (_ *connection*) (declare (ignore _)) , at body)
+    *connections*))
+
+(defun connection-names ()
+  "Return the list of all present connection names."
+  (let ((names '()))
+    (iterate-clients
+      (push *client-name* names))
+    names))
+
+(defmacro with-current-connection (&rest body)
+  `(let ((*connection* (get-connection)))
+     , at body))
+
+(defmacro with-connection-setting (var value &rest body)
+  (let ((accessor (intern (concat "CONNECTION-" (symbol-name var))))
+       (orig-value (gensym)))
+    `(let ((,orig-value (,accessor *connection*)))
+       (setf (,accessor *connection*) ,value)
+       (unwind-protect
+          (progn
+            , at body)
+        (setf (,accessor *connection*) ,orig-value)))))
+
+(defmacro with-connection-parameters (parameters &rest body)
+  (let (($parameters (gensym))
+        ($orig-parameters (gensym))
+        ($cparameters (gensym))
+        ($p (gensym))
+        ($v (gensym))
+        ($orig-v (gensym))
+        ($pv (gensym)))
+    `(let* ((,$parameters ,parameters)
+            (,$orig-parameters ()))
+       (unwind-protect
+           (progn
+             (while ,$parameters
+               (let* ((,$p (first ,$parameters))
+                      (,$v (second ,$parameters))
+                      (,$cparameters
+                       (connection-parameters *connection*))
+                      (,$orig-v (plist-get ,$cparameters ,$p)))
+                 (when (and (not (equal ,$v ,$orig-v))
+                            (or ,$v
+                                (not (member ,$p '(language)))))
+                   (when (plist-member ,$cparameters ,$p)
+                     (push (cons ,$p ,$orig-v) ,$orig-parameters))
+                   (set-parameter ,$p ,$v)))
+               (setq ,$parameters (nthcdr 2 ,$parameters)))
+             , at body)
+         (dolist (,$pv ,$orig-parameters)
+           (set-parameter (car ,$pv) (cdr ,$pv)))))))
+
+
+;;; Process management functions
+
+
+(defun get-connection (&optional (name *client-name*) (create-if-needed t))
+  (or (gethash name *connections*)
+      (and create-if-needed
+          (let ((*client-name* name))
+            (open-connection)))))
+
+(defun close-connection-stream (connection)
+  (let ((stream (connection-stream connection)))
+    (when stream
+      (ignore-errors (close-network-stream stream)))
+    (setf (connection-stream connection) nil)))
+
+(defun open-connection (&optional host port &key quiet force-reopen)
+  "Open SSIP connection to given HOST and PORT.
+If the connection corresponding to the current `*client-name*' value
+already exists, close it and reopen again, with the same connection parameters.
+
+The optional arguments HOST and PORT identify the speechd server location
+differing from the values of `speechd-host' and `speechd-port'.
+
+If the key argument QUIET is non-nil, don't report failures and quit silently.
+If the key argument FORCE-REOPEN is non-nil, try to reopen an existent
+connection even if it previously failed.
+
+Return the opened connection on success, nil otherwise."
+  (let ((connection (gethash *client-name* *connections*)))
+    (let ((host (or host *host*))
+         (port (or port *port*)))
+      (when connection
+       (close-connection connection)
+       (setq host (connection-host connection)
+             port (connection-port connection)))
+      (let* ((name *client-name*)
+             (default-parameters (append
+                                  (cdr (assoc *client-name*
+                                              *connection-parameters*
+                                              :test #'string=))
+                                  (cdr (assoc t *connection-parameters*))))
+            (parameters (if connection
+                             (append
+                              (connection-parameters connection)
+                              default-parameters)
+                             default-parameters))
+            (stream (when (or (not connection)
+                               (not (connection-failure-p connection))
+                               force-reopen)
+                       (ignore-errors
+                         (open-network-stream host port)))))
+       (when (and (not stream) (not quiet))
+          (error "Connection to SSIP failed"))
+       (setq connection (make-connection
+                         :name name :host host :port port
+                         :stream stream :failure-p (not stream)))
+       (setf (gethash name *connections*) connection)
+       (when stream
+         (set-connection-name name)
+          (setq parameters (append parameters
+                                   (list 'language *default-language*
+                                         'voice *default-voice*)))
+          (let ((already-set '(client-name)))
+            (while parameters
+              (destructuring-bind (parameter value . next) parameters
+                (unless (member parameter already-set)
+                  (push parameter already-set)
+                  (set-parameter parameter value))
+                (setq parameters next)))))
+        (let ((priority (and
+                         connection
+                         (plist-get default-parameters 'message-priority))))
+          (when priority
+            (set-parameter 'message-priority priority)
+            (setf (connection-forced-priority connection) t)))))
+    connection))
+
+(defun close-connection (&optional (name *client-name*))
+  "Close speechd connection named NAME."
+  (let ((connection (get-connection name nil)))
+    (when connection
+      (close-connection-stream connection)
+      (remhash name *connections*))))
+
+(defun reopen-connection ()
+  "Close and open again all the connections to speechd."
+  (iterate-clients (open-connection :quiet t :force-reopen t)))
+
+(defun running-p ()
+  "Return non-nil, if the current speechd client name process is running."
+  (let ((connection (get-connection)))
+    (and connection (connection-stream connection))))
+
+
+;;; Process communication functions
+
+
+(defun permanent-connection-failure (connection)
+  (close-connection-stream connection)
+  (setf (connection-failure-p connection) t
+       (connection-paused-p connection) nil
+       (connection-transaction-state connection) nil
+       (connection-parameters connection) ()))
+
+(defun send-string (string)
+  (with-current-connection
+    (let ((stream (connection-stream *connection*)))
+      (when stream
+       (unwind-protect
+             (format stream "~A" string)
+         (when (not (running-p))
+            (permanent-connection-failure *connection*)))))))
+
+(defun process-request (request)
+  (with-current-connection
+    ;; Ensure proper transaction state
+    (let* ((state-spec (request-transaction-state request))
+           (required-state (first state-spec))
+           (new-state (second state-spec)))
+      (labels ((check-state (reopen-if-needed)
+                 (let ((current-state (connection-transaction-state
+                                       *connection*)))
+                   (when (and (not (eq current-state required-state))
+                              (not (eq current-state new-state)))
+                     (cond
+                       ((and (eq required-state 'in-data)
+                             (not (eq new-state nil)))
+                        (send-data-begin))
+                       ((eq required-state nil)
+                        (send-data-end))))
+                   (setq current-state (connection-transaction-state
+                                        *connection*))
+                   (if (and reopen-if-needed
+                            (not (eq current-state required-state))
+                            (not (eq current-state new-state))
+                            (not (connection-failure-p *connection*)))
+                       (progn
+                         (open-connection)
+                         (setq *connection* (get-connection))
+                         (check-state nil))
+                       (eq current-state required-state)))))
+        ;; Continue only if the state can be set properly after reopen,
+        ;; otherwise give up and ignore the request completely.
+        ;; This also works for the "." command when in non-data state.
+        (when (check-state t)
+          (send-string (request-string request))
+          ;; Read command answer
+          (unless (equal state-spec '(in-data in-data))
+            (destructuring-bind (answer-line . data-lines)
+                (loop with stream = (connection-stream *connection*)
+                      for line = (read-line stream)
+                      for lines = (list line) then (cons line lines)
+                      while (and (> (length line) 3)
+                                 (char= (char line 3) #\-))
+                      finally (return lines))
+              (let* ((code (subseq answer-line 0 3))
+                     (answer (subseq answer-line 4))
+                     (success (member (char code 0) '(#\1 #\2)))
+                     (data (and success
+                                (mapcar #'(lambda (line) (subseq line 4))
+                                        data-lines))))
+                (when success
+                  (setf (connection-transaction-state *connection*) new-state))
+                (list success data code answer)))))))))
+
+(defun send-request (request)
+  (with-current-connection
+    (process-request request)))
+
+(defparameter +block-commands+
+  '(("speak")
+    ("sound_icon")
+    ("char")
+    ("key")
+    ("quit")
+    ("block" ("end"))
+    ("set" ("self" ("rate" "pitch" "voice" "language")))))
+
+(defun block-command-p (command &optional allowed)
+  (unless allowed
+    (setq allowed +block-commands+))
+  (let* ((match (assoc (first command) allowed :test #'string-equal))
+         (rest-allowed (cdr match)))
+    (and match
+         (or (not rest-allowed)
+             (block-command-p (rest command) rest-allowed)))))
+
+(defun send-command (command &optional (transaction-state '(nil nil)))
+  (unless (listp command)
+    (setq command (list command)))
+  (with-current-connection
+    (setf (connection-last-command *connection*) command)
+    (when (or (not (connection-in-block *connection*))
+              (block-command-p command))
+      (send-request
+        (make-request
+         :string (format nil "~{~A~^ ~}~A~A" command #\Return #\Linefeed)
+         :transaction-state transaction-state)))))
+
+(defun send-data-begin ()
+  (send-command "SPEAK" '(nil in-data)))
+
+(defun send-data (text)
+  (let ((text* text))
+    (flet ((send (string)
+             (unless (string= string "")
+               (send-request (make-request
+                              :string string
+                              :transaction-state '(in-data in-data))))))
+      (loop with eol = (format nil "~A~A" #\Return #\Linefeed)
+            for length = (length text*)
+            for nlpos = (or (position #\Linefeed text*) length)
+            for dotted = (and (> (length text*) 0)
+                              (char= (char text* 0) #\.))
+            until (string= text* "")
+            do (progn
+                 (when dotted
+                   (send "."))
+                 (send (subseq text* 0 nlpos))
+                 (send eol)
+                 (setq text* (subseq text* (min (1+ nlpos) length))))))))
+
+(defun send-data-end ()
+  (send-command "." '(in-data nil)))
+
+
+;;; Value retrieval functions
+
+
+(defun list-values (parameter)
+  (second (send-command
+          (list "LIST" (cdr (assoc parameter +list-parameter-names+))))))
+
+
+;;; Parameter setting functions
+
+
+(defun convert-numeric (number)
+  (cond ((< number -100) -100)
+       ((> number 100) 100)
+       (t number)))
+
+(defun transform-parameter-value (parameter value)
+  (cond
+   ((stringp value)
+    value)
+   ((integerp value)
+    (format nil "~D" (convert-numeric value)))
+   ((symbolp value)
+    (cdr (assoc value
+               (cdr (assoc parameter +parameter-value-mappings+)))))))
+
+(defun set-parameter (parameter value)
+  (with-current-connection
+    (let* ((plist (connection-parameters *connection*))
+          (orig-value (if (plist-member plist parameter)
+                          (plist-get plist parameter)
+                        'unknown)))
+      (when (or (member parameter +volatile-parameters+)
+                (and (not (equal orig-value value))
+                     (or (not (eq parameter 'message-priority))
+                         (not (connection-forced-priority *connection*)))))
+       (let ((answer
+              (send-command
+                (let ((p (cdr (assoc parameter +parameter-names+)))
+                      (v (transform-parameter-value parameter value)))
+                  (unless p
+                    (error "Invalid parameter name: `~A'" parameter))
+                  (unless v
+                    (error "Invalid parameter value: ~A=~A" parameter value))
+                  (list "SET" "self" p v)))))
+         (setq *connection* (get-connection))
+          (when (first answer)
+            (setf (connection-parameters *connection*)
+                  (plist-put (connection-parameters *connection*)
+                             parameter value))))))))
+
+(defun set-connection-name (name)
+  (set-parameter
+   'client-name
+   (format nil "~A:~A:~A" (user-login-name) *application-name* name)))
+
+(defun set-language (language)
+  "Set language of the current client connection to LANGUAGE.
+Language must be an RFC 1766 language code, as a string."
+  (set-parameter 'language language)
+  (setq *language* language))
+
+
+;;; Blocks
+
+
+(defmacro with-block (parameters &rest body)
+  "Set PARAMETERS and enclose BODY by an SSIP block.
+Before invoking BODY, the BLOCK BEGIN command is sent, and the BLOCK END
+command is sent afterwards.
+PARAMETERS is a property list defining parameters to be set before sending the
+BLOCK BEGIN command.  The property-value pairs correspond to the arguments of
+the `set-parameter' function."
+  `(with-current-connection
+     (with-connection-parameters ,parameters
+       (if (and *connection* (connection-in-block *connection*))
+           (progn , at body)
+         (let ((block-connection *connection*))
+           (send-command '("BLOCK BEGIN"))
+           (unwind-protect
+               (progn
+                 (with-current-connection
+                  (when *connection*
+                    (setf (connection-in-block *connection*) t)))
+                 , at body)
+             (let ((*connection* block-connection))
+               (when *connection*
+                 (setf (connection-in-block *connection*) nil)
+                 (let ((*client-name*
+                        (connection-name *connection*)))
+                   (send-command '("BLOCK END")))))))))))
+
+
+;;; Speaking functions
+
+
+(defun say-text (text &key (priority *default-text-priority*))
+  "Speak the given TEXT, represented by a string.
+The key argument `priority' defines the priority of the message and must be one
+of the symbols `important', `message', `text', `notification' or
+`progress'."
+  (set-parameter 'message-priority priority)
+  (unless (string= text "")
+    (send-data-begin)
+    (send-data text)
+    (send-data-end)))
+
+(defun say-sound (name &key (priority *default-sound-priority*))
+  "Play an auditory icon.
+NAME is the name of the icon, any string acceptable by speechd.
+The key argument `priority' defines the priority of the message and must be one
+of the symbols `important', `message', `text', `notification' or
+`progress'."
+  (set-parameter 'message-priority priority)
+  (send-command (list "SOUND_ICON" name)))
+
+(defun say-char (char &key (priority *default-char-priority*))
+  "Speak the given CHAR, any UTF-8 character.
+The key argument `priority' defines the priority of the message and must be one
+of the symbols `important', `message', `text', `notification' or
+`progress'."
+  (set-parameter 'message-priority priority)
+  (with-current-connection
+    (with-connection-parameters `(language ,*language*)
+      (send-command
+       (list "CHAR" (format nil "~A" (case char
+                                       (?  "space")
+                                       (?\n "linefeed")
+                                       (t (format nil "~A" char)))))))))
+
+
+;;; Control functions
+
+
+(defun control-command (command all &optional repeatable)
+  (cond
+   ((not all)
+    (when (or repeatable
+              (not (equal (first (connection-last-command (get-connection)))
+                          command)))
+      (send-command (list command "self"))))
+   ((numberp all)
+    (iterate-clients (control-command command nil)))
+   (t
+    (send-command (list command "all")))))
+
+(defun cancel (&optional all)
+  "Stop speaking all the messages sent through the current client so far.
+If the universal argument is given, stop speaking messages of all clients.
+If a numeric argument is given, stop speaking messages of all current Emacs
+session clients."
+  (control-command "CANCEL" all))
+
+(defun stop (&optional all)
+  "Stop speaking the currently spoken message (if any) of this client.
+If the optional argument ALL is non-nil, stop speaking the currently spoken
+messages of all clients."
+  (control-command "STOP" all t))
+
+(defun pause (&optional all)
+  "Pause speaking in the current client.
+If the optional argument ALL is non-nil, pause speaking in all clients."
+  (if all
+      (iterate-connections
+        (setf (connection-paused-p *connection*) t))
+    (setf (connection-paused-p (get-connection)) t))
+  (control-command "PAUSE" (not (not all))))
+
+(defun resume (&optional all)
+  "Resume previously stopped speaking in the current client.
+If the optional argument ALL is non-nil, resume speaking messages of all
+clients."
+  (when (or all (connection-paused-p (get-connection)))
+    (control-command "RESUME" (not (not all)))
+    (if all
+        (setf (connection-paused-p (get-connection)) nil)
+      (iterate-connections
+        (setf (connection-paused-p *connection*) nil)))))
diff --git a/src/api/cl/sysdep.lisp b/src/api/cl/sysdep.lisp
new file mode 100644
index 0000000..5232986
--- /dev/null
+++ b/src/api/cl/sysdep.lisp
@@ -0,0 +1,54 @@
+;;; System dependent functions
+
+;; Author: Milan Zamazal <pdm at brailcom.org>
+
+;; Copyright (C) 2004 Brailcom, o.p.s.
+
+;; COPYRIGHT NOTICE
+
+;; 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 2 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+;; for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program; if not, write to the Free Software
+;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+
+
+(in-package :ssip)
+
+
+(defun getenv (var)
+  #+SBCL
+  (sb-ext:posix-getenv var)
+  #+CLISP
+  (ext:getenv var))
+
+
+#+CLISP
+(defparameter +encoding+
+  (ext:make-encoding :charset 'charset:utf-8 :line-terminator :unix))
+
+(defun open-network-stream (host port)
+  #+CLISP
+  (socket:socket-connect port host :external-format +encoding+)
+  #+SBCL
+  (let ((s (make-instance 'sb-bsd-sockets:inet-socket :type :stream
+                          :protocol :tcp)))
+    (sb-bsd-sockets:socket-connect
+     s
+     (sb-bsd-sockets:host-ent-address (sb-bsd-sockets:get-host-by-name host))
+     port)
+    (sb-bsd-sockets:socket-make-stream s :input t :output t :buffering :none))
+  )
+
+(defun close-network-stream (stream)
+  #+(or SBCL CLISP)
+  (close stream))
+
diff --git a/src/cl/ChangeLog b/src/cl/ChangeLog
deleted file mode 100644
index 1139b83..0000000
--- a/src/cl/ChangeLog
+++ /dev/null
@@ -1,8 +0,0 @@
-2004-01-20  Milan Zamazal  <pdm at brailcom.org>
-
-       * sysdep.lisp (open-network-stream): Socket creation for SBCL
-       fixed.
-
-       * ssip.lisp: Exportable symbolic parameter values converted to
-       keywords.
-
diff --git a/src/cl/README b/src/cl/README
deleted file mode 100644
index ea73f4c..0000000
--- a/src/cl/README
+++ /dev/null
@@ -1,16 +0,0 @@
-This is a simple Common Lisp interface to Speech Dispatcher through the Speech
-Synthesis Interface Protocol (SSIP).
-
-Currently, there is no documentation, read the sources.
-The simplest use can be as follows:
-
-  (asdf:operate-on-system 'asdf:load-op :ssip)
-  (ssip:say-text "Hello, world!")
-
-It works with CLisp and SBCL, but it should be easy to port to other systems,
-just add the support to sysdep.lisp.
-
-Feel free to contact us with any questions regarding the Common Lisp interface
-at the Speech Dispatcher mailing list <speechd at lists.freebsoft.org>.
-
--- Milan Zamazal <pdm at freebsoft.org>
diff --git a/src/cl/configuration.lisp b/src/cl/configuration.lisp
deleted file mode 100644
index 49a43f8..0000000
--- a/src/cl/configuration.lisp
+++ /dev/null
@@ -1,64 +0,0 @@
-;;; Configuration variables
-
-;; Author: Milan Zamazal <pdm at brailcom.org>
-
-;; Copyright (C) 2004 Brailcom, o.p.s.
-
-;; COPYRIGHT NOTICE
-
-;; 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 2 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-;; for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
-
-
-(in-package :ssip)
-
-
-(defvar *host* "localhost"
-  "Name of the default host running speechd to connect to.")
-
-(defvar *port* (or (ignore-errors
-                     (car (read-from-string (getenv "SPEECHD_PORT"))))
-                   6560)
-  "Default port of speechd.")
-
-
-(defvar *default-text-priority* :text
-  "Default Speech Dispatcher priority of sent texts.")
-
-(defvar *default-sound-priority* :message
-  "Default Speech Dispatcher priority of sent sound icons.")
-
-(defvar *default-char-priority* :notification
-  "Default Speech Dispatcher priority of sent single letters.")
-
-
-(defvar *connection-parameters* '()
-  "Alist of connection names and their parameters.
-
-Each element of the list is of the form (CONNECTION-NAME . PARAMETERS), where
-CONNECTION-NAME is a connection name as expected to be in `speechd-client-name'
-and PARAMETERS is a property list with the pairs of parameter identifiers and
-parameter values.  Valid parameter names are the following symbols:
-language, message-priority, punctuation-mode, capital-character-mode, voice,
-rate, pitch, output-module.  See the corresponding speechd-set-* functions for
-valid parameter values.
-
-If the symbol t is specified as the connection name, the element defines
-default connection parameters if no connection specification applies.  Only one
-such an element is allowed in the whole alist.
-
-The message-priority parameter has a special meaning: It overrides priority of
-all messages sent through the connection.
-
-You must reopen the connections to apply the changes to this variable.")
diff --git a/src/cl/elisp.lisp b/src/cl/elisp.lisp
deleted file mode 100644
index cf1cc2c..0000000
--- a/src/cl/elisp.lisp
+++ /dev/null
@@ -1,61 +0,0 @@
-;;; Elisp compatibility functions
-
-;; Author: Milan Zamazal <pdm at brailcom.org>
-
-;; Copyright (C) 2004 Brailcom, o.p.s.
-
-;; COPYRIGHT NOTICE
-
-;; 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 2 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-;; for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
-
-
-(in-package :ssip)
-
-
-(defmacro while (condition &body body)
-  `(loop while ,condition
-         do (progn , at body)))
-
-
-(defun plist-get-internal (plist prop)
-  (cond
-    ((null plist)
-     nil)
-    ((eq (car plist) prop)
-     (cdr plist))
-    (t
-     (plist-get-internal (nthcdr 2 plist) prop))))
-  
-(defun plist-get (plist prop)
-  (first (plist-get-internal plist prop)))
-
-(defun plist-member (plist prop)
-  (not (null (plist-get-internal plist prop))))
-
-(defun plist-put (plist prop val)
-  (let ((value (plist-get-internal plist prop)))
-    (if value
-        (progn
-          (rplaca value val)
-          plist)
-        (list* prop val plist))))
-
-
-(defun user-login-name ()
-  (or (getenv "LOGNAME") (getenv "USER")))
-
-
-(defun concat (&rest args)
-  (apply #'concatenate 'string args))
diff --git a/src/cl/package.lisp b/src/cl/package.lisp
deleted file mode 100644
index 9a1be15..0000000
--- a/src/cl/package.lisp
+++ /dev/null
@@ -1,49 +0,0 @@
-;;; Package definition
-
-;; Author: Milan Zamazal <pdm at brailcom.org>
-
-;; Copyright (C) 2004 Brailcom, o.p.s.
-
-;; COPYRIGHT NOTICE
-
-;; 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 2 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-;; for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
-
-
-(in-package :cl-user)
-
-
-(defpackage :ssip
-  (:use :cl)
-  (:export
-   ;; configuration.lisp
-   #:*application-name*
-   #:*client-name*
-   #:*language*
-   #:*spell*
-   ;; ssip.lisp
-   #:connection-names
-   #:set-language
-   #:open-connection
-   #:close-connection
-   #:reopen-connection
-   #:say-text
-   #:say-sound
-   #:say-char
-   #:cancel
-   #:stop
-   #:pause
-   #:resume
-   ))
-
diff --git a/src/cl/ssip.asd b/src/cl/ssip.asd
deleted file mode 100644
index 690fed0..0000000
--- a/src/cl/ssip.asd
+++ /dev/null
@@ -1,30 +0,0 @@
-;; Copyright (C) 2004 Brailcom, o.p.s.
-
-;; COPYRIGHT NOTICE
-
-;; 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 2 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-;; for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
-
-
-(in-package :asdf)
-
-
-(defsystem :ssip :depends-on (:regex #+SBCL :sb-bsd-sockets)
-  :components
-  ((:file "package")
-   (:file "sysdep")
-   (:file "elisp")
-   (:file "configuration")
-   (:file "ssip"))
-  :serial t)
diff --git a/src/cl/ssip.lisp b/src/cl/ssip.lisp
deleted file mode 100644
index 3a2cddf..0000000
--- a/src/cl/ssip.lisp
+++ /dev/null
@@ -1,613 +0,0 @@
-;;; Speech Synthesis Interface Protocol (SSIP) interface
-
-;; Author: Milan Zamazal <pdm at brailcom.org>
-
-;; Copyright (C) 2004 Brailcom, o.p.s.
-
-;; COPYRIGHT NOTICE
-
-;; 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 2 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-;; for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
-
-
-;;; Note: This library was ported from the Elisp library, so don't wonder much
-;;; about elispisms found here...
-
-
-(in-package :ssip)
-
-
-;;; Exported variables
-
-
-(defvar *application-name* "lisp"
-  "String defining current application name.")
-  
-(defvar *client-name* "default"
-  "String defining current client name.
-This variable's value defines which connection is used when communicating via
-SSIP, each connection has its own client name.  Usually, you select the proper
-client (connection) by assigning a value to this variable locally through
-`let'.")
-
-(defvar *language* nil
-  "If non-nil, it is an RFC 1766 language code, as a string.
-If text is read and this variable is non-nil, the text is read in the given
-language.")
-
-(defvar *spell* nil
-  "If non-nil, any spoken text is spelled.")
-
-
-;;; Internal constants and configuration variables
-
-
-(defparameter +version+ "$Id: ssip.lisp,v 1.3 2006-02-17 13:18:55 pdm Exp $"
-  "Version stamp of the source file.
-Useful only for diagnosing problems.")
-
-(defvar *language-codes*
-  '(("czech" . "cs")
-    ("english" . "en")
-    ("french" . "fr")
-    ("german" . "de"))
-  "Mapping of LANG values to language ISO codes.")
-
-(defvar *default-voice* "male1")
-(defvar *default-language* (or (cdr (assoc (getenv "LANG") *language-codes*
-                                           :test #'string=))
-                               "en"))
-
-(defparameter +parameter-names+
-  '((client-name . "CLIENT_NAME")
-    (language . "LANGUAGE")
-    (message-priority . "PRIORITY")
-    (punctuation-mode . "PUNCTUATION")
-    (pause-context . "PAUSE_CONTEXT")
-    (capital-character-mode . "CAP_LET_RECOGN")
-    (voice . "VOICE")
-    (rate . "RATE")
-    (pitch . "PITCH")
-    (spelling-mode . "SPELLING")
-    (output-module . "OUTPUT_MODULE")
-    ))
-
-(defparameter +list-parameter-names+
-  '((voices . "VOICES")))
-
-(defparameter +parameter-value-mappings+
-  '((message-priority
-     (:important .    "IMPORTANT")
-     (:message .      "MESSAGE")
-     (:text .         "TEXT")
-     (:notification . "NOTIFICATION")
-     (:progress .     "PROGRESS")
-     )
-    (punctuation-mode
-     (:none . "none")
-     (:some . "some")
-     (:all .  "all"))
-    (capital-character-mode
-     (:none . "none")
-     (:spell . "spell")
-     (:icon . "icon"))
-    (spelling-mode
-     (t . "on")
-     (nil . "off"))))
-
-(defparameter +volatile-parameters+ '(output-module))
-
-(defparameter +punctuation-modes+ '(("none" . none)
-                                    ("some" . some)
-                                    ("all" .  all)))
-
-(defparameter +capital-character-modes+ '(("none" .  none)
-                                          ("spell" . spell)
-                                          ("icon" .  icon)))
-
-
-;;; Internal variables
-
-
-(defstruct connection
-  name
-  host
-  port
-  (failure-p nil)
-  stream
-  (paused-p nil)
-  (in-block nil)
-  (transaction-state nil)
-  (parameters ())
-  (forced-priority nil)
-  (last-command nil))
-
-(defstruct request
-  string
-  (transaction-state '(nil nil)))
-
-(defvar *connections* (make-hash-table :test #'equal)
-  "Hash table mapping client names to `connection' instances.")
-
-(defvar *connection* nil
-  "Current connection.")
-
-
-
-;;; Utilities
-
-
-(defmacro iterate-clients (&rest body)
-  `(maphash #'(lambda (*client-name* _) (declare (ignore _)) , at body)
-    *connections*))
-
-(defmacro iterate-connections (&rest body)
-  `(maphash #'(lambda (_ *connection*) (declare (ignore _)) , at body)
-    *connections*))
-
-(defun connection-names ()
-  "Return the list of all present connection names."
-  (let ((names '()))
-    (iterate-clients
-      (push *client-name* names))
-    names))
-
-(defmacro with-current-connection (&rest body)
-  `(let ((*connection* (get-connection)))
-     , at body))
-
-(defmacro with-connection-setting (var value &rest body)
-  (let ((accessor (intern (concat "CONNECTION-" (symbol-name var))))
-       (orig-value (gensym)))
-    `(let ((,orig-value (,accessor *connection*)))
-       (setf (,accessor *connection*) ,value)
-       (unwind-protect
-          (progn
-            , at body)
-        (setf (,accessor *connection*) ,orig-value)))))
-
-(defmacro with-connection-parameters (parameters &rest body)
-  (let (($parameters (gensym))
-        ($orig-parameters (gensym))
-        ($cparameters (gensym))
-        ($p (gensym))
-        ($v (gensym))
-        ($orig-v (gensym))
-        ($pv (gensym)))
-    `(let* ((,$parameters ,parameters)
-            (,$orig-parameters ()))
-       (unwind-protect
-           (progn
-             (while ,$parameters
-               (let* ((,$p (first ,$parameters))
-                      (,$v (second ,$parameters))
-                      (,$cparameters
-                       (connection-parameters *connection*))
-                      (,$orig-v (plist-get ,$cparameters ,$p)))
-                 (when (and (not (equal ,$v ,$orig-v))
-                            (or ,$v
-                                (not (member ,$p '(language)))))
-                   (when (plist-member ,$cparameters ,$p)
-                     (push (cons ,$p ,$orig-v) ,$orig-parameters))
-                   (set-parameter ,$p ,$v)))
-               (setq ,$parameters (nthcdr 2 ,$parameters)))
-             , at body)
-         (dolist (,$pv ,$orig-parameters)
-           (set-parameter (car ,$pv) (cdr ,$pv)))))))
-
-
-;;; Process management functions
-
-
-(defun get-connection (&optional (name *client-name*) (create-if-needed t))
-  (or (gethash name *connections*)
-      (and create-if-needed
-          (let ((*client-name* name))
-            (open-connection)))))
-
-(defun close-connection-stream (connection)
-  (let ((stream (connection-stream connection)))
-    (when stream
-      (ignore-errors (close-network-stream stream)))
-    (setf (connection-stream connection) nil)))
-
-(defun open-connection (&optional host port &key quiet force-reopen)
-  "Open SSIP connection to given HOST and PORT.
-If the connection corresponding to the current `*client-name*' value
-already exists, close it and reopen again, with the same connection parameters.
-
-The optional arguments HOST and PORT identify the speechd server location
-differing from the values of `speechd-host' and `speechd-port'.
-
-If the key argument QUIET is non-nil, don't report failures and quit silently.
-If the key argument FORCE-REOPEN is non-nil, try to reopen an existent
-connection even if it previously failed.
-
-Return the opened connection on success, nil otherwise."
-  (let ((connection (gethash *client-name* *connections*)))
-    (let ((host (or host *host*))
-         (port (or port *port*)))
-      (when connection
-       (close-connection connection)
-       (setq host (connection-host connection)
-             port (connection-port connection)))
-      (let* ((name *client-name*)
-             (default-parameters (append
-                                  (cdr (assoc *client-name*
-                                              *connection-parameters*
-                                              :test #'string=))
-                                  (cdr (assoc t *connection-parameters*))))
-            (parameters (if connection
-                             (append
-                              (connection-parameters connection)
-                              default-parameters)
-                             default-parameters))
-            (stream (when (or (not connection)
-                               (not (connection-failure-p connection))
-                               force-reopen)
-                       (ignore-errors
-                         (open-network-stream host port)))))
-       (when (and (not stream) (not quiet))
-          (error "Connection to SSIP failed"))
-       (setq connection (make-connection
-                         :name name :host host :port port
-                         :stream stream :failure-p (not stream)))
-       (setf (gethash name *connections*) connection)
-       (when stream
-         (set-connection-name name)
-          (setq parameters (append parameters
-                                   (list 'language *default-language*
-                                         'voice *default-voice*)))
-          (let ((already-set '(client-name)))
-            (while parameters
-              (destructuring-bind (parameter value . next) parameters
-                (unless (member parameter already-set)
-                  (push parameter already-set)
-                  (set-parameter parameter value))
-                (setq parameters next)))))
-        (let ((priority (and
-                         connection
-                         (plist-get default-parameters 'message-priority))))
-          (when priority
-            (set-parameter 'message-priority priority)
-            (setf (connection-forced-priority connection) t)))))
-    connection))
-
-(defun close-connection (&optional (name *client-name*))
-  "Close speechd connection named NAME."
-  (let ((connection (get-connection name nil)))
-    (when connection
-      (close-connection-stream connection)
-      (remhash name *connections*))))
-
-(defun reopen-connection ()
-  "Close and open again all the connections to speechd."
-  (iterate-clients (open-connection :quiet t :force-reopen t)))
-
-(defun running-p ()
-  "Return non-nil, if the current speechd client name process is running."
-  (let ((connection (get-connection)))
-    (and connection (connection-stream connection))))
-
-
-;;; Process communication functions
-
-
-(defun permanent-connection-failure (connection)
-  (close-connection-stream connection)
-  (setf (connection-failure-p connection) t
-       (connection-paused-p connection) nil
-       (connection-transaction-state connection) nil
-       (connection-parameters connection) ()))
-
-(defun send-string (string)
-  (with-current-connection
-    (let ((stream (connection-stream *connection*)))
-      (when stream
-       (unwind-protect
-             (format stream "~A" string)
-         (when (not (running-p))
-            (permanent-connection-failure *connection*)))))))
-
-(defun process-request (request)
-  (with-current-connection
-    ;; Ensure proper transaction state
-    (let* ((state-spec (request-transaction-state request))
-           (required-state (first state-spec))
-           (new-state (second state-spec)))
-      (labels ((check-state (reopen-if-needed)
-                 (let ((current-state (connection-transaction-state
-                                       *connection*)))
-                   (when (and (not (eq current-state required-state))
-                              (not (eq current-state new-state)))
-                     (cond
-                       ((and (eq required-state 'in-data)
-                             (not (eq new-state nil)))
-                        (send-data-begin))
-                       ((eq required-state nil)
-                        (send-data-end))))
-                   (setq current-state (connection-transaction-state
-                                        *connection*))
-                   (if (and reopen-if-needed
-                            (not (eq current-state required-state))
-                            (not (eq current-state new-state))
-                            (not (connection-failure-p *connection*)))
-                       (progn
-                         (open-connection)
-                         (setq *connection* (get-connection))
-                         (check-state nil))
-                       (eq current-state required-state)))))
-        ;; Continue only if the state can be set properly after reopen,
-        ;; otherwise give up and ignore the request completely.
-        ;; This also works for the "." command when in non-data state.
-        (when (check-state t)
-          (send-string (request-string request))
-          ;; Read command answer
-          (unless (equal state-spec '(in-data in-data))
-            (destructuring-bind (answer-line . data-lines)
-                (loop with stream = (connection-stream *connection*)
-                      for line = (read-line stream)
-                      for lines = (list line) then (cons line lines)
-                      while (and (> (length line) 3)
-                                 (char= (char line 3) #\-))
-                      finally (return lines))
-              (let* ((code (subseq answer-line 0 3))
-                     (answer (subseq answer-line 4))
-                     (success (member (char code 0) '(#\1 #\2)))
-                     (data (and success
-                                (mapcar #'(lambda (line) (subseq line 4))
-                                        data-lines))))
-                (when success
-                  (setf (connection-transaction-state *connection*) new-state))
-                (list success data code answer)))))))))
-
-(defun send-request (request)
-  (with-current-connection
-    (process-request request)))
-
-(defparameter +block-commands+
-  '(("speak")
-    ("sound_icon")
-    ("char")
-    ("key")
-    ("quit")
-    ("block" ("end"))
-    ("set" ("self" ("rate" "pitch" "voice" "language")))))
-
-(defun block-command-p (command &optional allowed)
-  (unless allowed
-    (setq allowed +block-commands+))
-  (let* ((match (assoc (first command) allowed :test #'string-equal))
-         (rest-allowed (cdr match)))
-    (and match
-         (or (not rest-allowed)
-             (block-command-p (rest command) rest-allowed)))))
-
-(defun send-command (command &optional (transaction-state '(nil nil)))
-  (unless (listp command)
-    (setq command (list command)))
-  (with-current-connection
-    (setf (connection-last-command *connection*) command)
-    (when (or (not (connection-in-block *connection*))
-              (block-command-p command))
-      (send-request
-        (make-request
-         :string (format nil "~{~A~^ ~}~A~A" command #\Return #\Linefeed)
-         :transaction-state transaction-state)))))
-
-(defun send-data-begin ()
-  (send-command "SPEAK" '(nil in-data)))
-
-(defun send-data (text)
-  (let ((text* text))
-    (flet ((send (string)
-             (unless (string= string "")
-               (send-request (make-request
-                              :string string
-                              :transaction-state '(in-data in-data))))))
-      (loop with eol = (format nil "~A~A" #\Return #\Linefeed)
-            for length = (length text*)
-            for nlpos = (or (position #\Linefeed text*) length)
-            for dotted = (and (> (length text*) 0)
-                              (char= (char text* 0) #\.))
-            until (string= text* "")
-            do (progn
-                 (when dotted
-                   (send "."))
-                 (send (subseq text* 0 nlpos))
-                 (send eol)
-                 (setq text* (subseq text* (min (1+ nlpos) length))))))))
-
-(defun send-data-end ()
-  (send-command "." '(in-data nil)))
-
-
-;;; Value retrieval functions
-
-
-(defun list-values (parameter)
-  (second (send-command
-          (list "LIST" (cdr (assoc parameter +list-parameter-names+))))))
-
-
-;;; Parameter setting functions
-
-
-(defun convert-numeric (number)
-  (cond ((< number -100) -100)
-       ((> number 100) 100)
-       (t number)))
-
-(defun transform-parameter-value (parameter value)
-  (cond
-   ((stringp value)
-    value)
-   ((integerp value)
-    (format nil "~D" (convert-numeric value)))
-   ((symbolp value)
-    (cdr (assoc value
-               (cdr (assoc parameter +parameter-value-mappings+)))))))
-
-(defun set-parameter (parameter value)
-  (with-current-connection
-    (let* ((plist (connection-parameters *connection*))
-          (orig-value (if (plist-member plist parameter)
-                          (plist-get plist parameter)
-                        'unknown)))
-      (when (or (member parameter +volatile-parameters+)
-                (and (not (equal orig-value value))
-                     (or (not (eq parameter 'message-priority))
-                         (not (connection-forced-priority *connection*)))))
-       (let ((answer
-              (send-command
-                (let ((p (cdr (assoc parameter +parameter-names+)))
-                      (v (transform-parameter-value parameter value)))
-                  (unless p
-                    (error "Invalid parameter name: `~A'" parameter))
-                  (unless v
-                    (error "Invalid parameter value: ~A=~A" parameter value))
-                  (list "SET" "self" p v)))))
-         (setq *connection* (get-connection))
-          (when (first answer)
-            (setf (connection-parameters *connection*)
-                  (plist-put (connection-parameters *connection*)
-                             parameter value))))))))
-
-(defun set-connection-name (name)
-  (set-parameter
-   'client-name
-   (format nil "~A:~A:~A" (user-login-name) *application-name* name)))
-
-(defun set-language (language)
-  "Set language of the current client connection to LANGUAGE.
-Language must be an RFC 1766 language code, as a string."
-  (set-parameter 'language language)
-  (setq *language* language))
-
-
-;;; Blocks
-
-
-(defmacro with-block (parameters &rest body)
-  "Set PARAMETERS and enclose BODY by an SSIP block.
-Before invoking BODY, the BLOCK BEGIN command is sent, and the BLOCK END
-command is sent afterwards.
-PARAMETERS is a property list defining parameters to be set before sending the
-BLOCK BEGIN command.  The property-value pairs correspond to the arguments of
-the `set-parameter' function."
-  `(with-current-connection
-     (with-connection-parameters ,parameters
-       (if (and *connection* (connection-in-block *connection*))
-           (progn , at body)
-         (let ((block-connection *connection*))
-           (send-command '("BLOCK BEGIN"))
-           (unwind-protect
-               (progn
-                 (with-current-connection
-                  (when *connection*
-                    (setf (connection-in-block *connection*) t)))
-                 , at body)
-             (let ((*connection* block-connection))
-               (when *connection*
-                 (setf (connection-in-block *connection*) nil)
-                 (let ((*client-name*
-                        (connection-name *connection*)))
-                   (send-command '("BLOCK END")))))))))))
-
-
-;;; Speaking functions
-
-
-(defun say-text (text &key (priority *default-text-priority*))
-  "Speak the given TEXT, represented by a string.
-The key argument `priority' defines the priority of the message and must be one
-of the symbols `important', `message', `text', `notification' or
-`progress'."
-  (set-parameter 'message-priority priority)
-  (unless (string= text "")
-    (send-data-begin)
-    (send-data text)
-    (send-data-end)))
-
-(defun say-sound (name &key (priority *default-sound-priority*))
-  "Play an auditory icon.
-NAME is the name of the icon, any string acceptable by speechd.
-The key argument `priority' defines the priority of the message and must be one
-of the symbols `important', `message', `text', `notification' or
-`progress'."
-  (set-parameter 'message-priority priority)
-  (send-command (list "SOUND_ICON" name)))
-
-(defun say-char (char &key (priority *default-char-priority*))
-  "Speak the given CHAR, any UTF-8 character.
-The key argument `priority' defines the priority of the message and must be one
-of the symbols `important', `message', `text', `notification' or
-`progress'."
-  (set-parameter 'message-priority priority)
-  (with-current-connection
-    (with-connection-parameters `(language ,*language*)
-      (send-command
-       (list "CHAR" (format nil "~A" (case char
-                                       (?  "space")
-                                       (?\n "linefeed")
-                                       (t (format nil "~A" char)))))))))
-
-
-;;; Control functions
-
-
-(defun control-command (command all &optional repeatable)
-  (cond
-   ((not all)
-    (when (or repeatable
-              (not (equal (first (connection-last-command (get-connection)))
-                          command)))
-      (send-command (list command "self"))))
-   ((numberp all)
-    (iterate-clients (control-command command nil)))
-   (t
-    (send-command (list command "all")))))
-
-(defun cancel (&optional all)
-  "Stop speaking all the messages sent through the current client so far.
-If the universal argument is given, stop speaking messages of all clients.
-If a numeric argument is given, stop speaking messages of all current Emacs
-session clients."
-  (control-command "CANCEL" all))
-
-(defun stop (&optional all)
-  "Stop speaking the currently spoken message (if any) of this client.
-If the optional argument ALL is non-nil, stop speaking the currently spoken
-messages of all clients."
-  (control-command "STOP" all t))
-
-(defun pause (&optional all)
-  "Pause speaking in the current client.
-If the optional argument ALL is non-nil, pause speaking in all clients."
-  (if all
-      (iterate-connections
-        (setf (connection-paused-p *connection*) t))
-    (setf (connection-paused-p (get-connection)) t))
-  (control-command "PAUSE" (not (not all))))
-
-(defun resume (&optional all)
-  "Resume previously stopped speaking in the current client.
-If the optional argument ALL is non-nil, resume speaking messages of all
-clients."
-  (when (or all (connection-paused-p (get-connection)))
-    (control-command "RESUME" (not (not all)))
-    (if all
-        (setf (connection-paused-p (get-connection)) nil)
-      (iterate-connections
-        (setf (connection-paused-p *connection*) nil)))))
diff --git a/src/cl/sysdep.lisp b/src/cl/sysdep.lisp
deleted file mode 100644
index 5232986..0000000
--- a/src/cl/sysdep.lisp
+++ /dev/null
@@ -1,54 +0,0 @@
-;;; System dependent functions
-
-;; Author: Milan Zamazal <pdm at brailcom.org>
-
-;; Copyright (C) 2004 Brailcom, o.p.s.
-
-;; COPYRIGHT NOTICE
-
-;; 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 2 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-;; for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program; if not, write to the Free Software
-;; Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
-
-
-(in-package :ssip)
-
-
-(defun getenv (var)
-  #+SBCL
-  (sb-ext:posix-getenv var)
-  #+CLISP
-  (ext:getenv var))
-
-
-#+CLISP
-(defparameter +encoding+
-  (ext:make-encoding :charset 'charset:utf-8 :line-terminator :unix))
-
-(defun open-network-stream (host port)
-  #+CLISP
-  (socket:socket-connect port host :external-format +encoding+)
-  #+SBCL
-  (let ((s (make-instance 'sb-bsd-sockets:inet-socket :type :stream
-                          :protocol :tcp)))
-    (sb-bsd-sockets:socket-connect
-     s
-     (sb-bsd-sockets:host-ent-address (sb-bsd-sockets:get-host-by-name host))
-     port)
-    (sb-bsd-sockets:socket-make-stream s :input t :output t :buffering :none))
-  )
-
-(defun close-network-stream (stream)
-  #+(or SBCL CLISP)
-  (close stream))
-
-- 
1.7.2.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]