#REFACTORING by exept
authorClaus Gittinger <cg@exept.de>
Sun, 05 Jan 2020 17:35:48 +0100
changeset 5421 9249f574dc60
parent 5420 ed59dfaab1a8
child 5422 44f59b02e13f
#REFACTORING by exept class: SoundStream class added: #defaultClass #defaultClass: comment/format in: #determineConcreteClass changed: #new
SoundStream.st
--- a/SoundStream.st	Sun Jan 05 17:26:45 2020 +0100
+++ b/SoundStream.st	Sun Jan 05 17:35:48 2020 +0100
@@ -38,6 +38,13 @@
 	privateIn:SoundStream
 !
 
+SoundStream subclass:#JackAudio
+	instanceVariableNames:''
+	classVariableNames:''
+	poolDictionaries:''
+	privateIn:SoundStream
+!
+
 SoundStream subclass:#PortAudio
 	instanceVariableNames:''
 	classVariableNames:''
@@ -64,42 +71,40 @@
 
 #include "stxOSDefs.h"
 
-#ifdef __win32__
-# ifndef NO_SOUND
+#ifndef NO_SOUND
+
+# ifdef __win32__
 #  define xxxSUPPORT_WIN32_DIRECTSOUND
 #  ifndef SUPPORT_WIN32_WAVESOUND
 #   define SUPPORT_WIN32_WAVESOUND
 #  endif
-# endif
-#endif
-
-#ifdef __iris__
-# ifndef IRIX5
-#  ifndef SUPPORT_IRIS_AUDIO
-#   define SUPPORT_IRIS_AUDIO
-#  endif
-# endif
-#endif
-
-#ifndef SUPPORT_IRIS_AUDIO
-# ifndef SUPPORT_ALSA_AUDIO
-#  ifdef LINUX
-#   ifndef SUPPORT_DEV_AUDIO
-#    define SUPPORT_DEV_AUDIO
+# endif /* __win32__ */
+
+# ifdef __iris__
+#  ifndef IRIX5
+#   ifndef SUPPORT_IRIS_AUDIO
+#    define SUPPORT_IRIS_AUDIO
 #   endif
 #  endif
-# endif
-#endif
-
-#ifndef NO_SOUND
-# ifndef USED_AUDIO
-#  ifdef __osx__
-#   ifndef SUPPORT_PORTAUDIO
-#    define SUPPORT_PORTAUDIO
-#   endif
+# endif // __iris__
+
+# ifndef SUPPORT_IRIS_AUDIO
+#  ifndef SUPPORT_ALSA_AUDIO
+#   ifdef LINUX
+#    ifndef SUPPORT_DEV_AUDIO
+#     define SUPPORT_DEV_AUDIO
+#    endif
+#   endif // LINUX
 #  endif
-# endif
-#endif
+# endif // SUPPORT_IRIS_AUDIO
+
+# ifdef __osx__
+#  ifndef SUPPORT_PORTAUDIO
+#   define SUPPORT_PORTAUDIO
+#  endif
+# endif // __osx__
+
+#endif /* NO_SOUND */
 
 #ifdef SUPPORT_IRIS_AUDIO
 # include <audio.h>
@@ -177,6 +182,10 @@
 
 #endif // SUPPORT_PORTAUDIO
 
+#ifdef SUPPORT_JACKAUDIO
+# include "jack/jack.h"
+#endif // SUPPORT_JACKAUDIO
+
 #ifdef __win32__
 # define _WIN32
 
@@ -301,6 +310,12 @@
 %}
 ! !
 
+!SoundStream primitiveVariables!
+%{
+static int DEBUGGING = 0;
+%}
+! !
+
 !SoundStream primitiveFunctions!
 %{
 
@@ -330,8 +345,6 @@
 
 #ifdef SUPPORT_PORTAUDIO
 
-static int DEBUGGING = 0;
-
 struct paBuffer {
     void* sampleData;
     int bufferSize;
@@ -352,7 +365,7 @@
 
 /*
  * This routine will be called by the PortAudio engine when audio is needed.
- * It may called at interrupt level on some machines so don't do anything
+ * It may called at interrupt level on some machines, so don't do anything
  * that could mess up the system like calling malloc() or free().
  */
 static int
@@ -362,29 +375,29 @@
 	    PaStreamCallbackFlags statusFlags,
 	    void *userData )
 {
-    struct paStreamData *paStreamData = (struct paStreamData *)userData;
-    struct paBuffer *paBuffer = paStreamData->currentBuffer;
-    int nBytesToOutput = paStreamData->bytesPerSample * paStreamData->nChannels * framesPerBuffer;
+    struct paStreamData *streamData = (struct paStreamData *)userData;
+    struct paBuffer *buffer = streamData->currentBuffer;
+    int nBytesToOutput = streamData->bytesPerSample * streamData->nChannels * framesPerBuffer;
     int nBytesLeftInBuffer;
     int readOffset;
     int writeOffset = 0;
     int nSent;
     int retVal = paContinue;
 
-    readOffset = paStreamData->readOffset;
+    readOffset = streamData->readOffset;
   again:
-    if (paBuffer != NULL) {
-	nBytesLeftInBuffer = paBuffer->bufferSize - readOffset;
+    if (buffer != NULL) {
+	nBytesLeftInBuffer = buffer->bufferSize - readOffset;
 	if (DEBUGGING) {
 	    fprintf(stderr, "pa: %d from %p[%d] to [%d]; next: %p last: %p)\n",
-			nBytesLeftInBuffer, paBuffer, readOffset, writeOffset,
-			paBuffer->nextBuffer, paStreamData->lastBuffer);
+			nBytesLeftInBuffer, buffer, readOffset, writeOffset,
+			buffer->nextBuffer, streamData->lastBuffer);
 	}
     } else {
 	nBytesLeftInBuffer = 0;
     }
     if (nBytesLeftInBuffer >= nBytesToOutput) {
-	memcpy(outputBuffer+writeOffset, paBuffer->sampleData+readOffset, nBytesToOutput);
+	memcpy(outputBuffer+writeOffset, buffer->sampleData+readOffset, nBytesToOutput);
 	nSent = nBytesToOutput;
 	if (DEBUGGING) {
 	    fprintf(stderr, "pa: %d from buffer\n", nSent);
@@ -394,7 +407,7 @@
     } else {
 	nSent = nBytesLeftInBuffer;
 	if (nSent > 0) {
-	    memcpy(outputBuffer+writeOffset, paBuffer->sampleData+readOffset, nBytesLeftInBuffer);
+	    memcpy(outputBuffer+writeOffset, buffer->sampleData+readOffset, nBytesLeftInBuffer);
 	    if (DEBUGGING) {
 		fprintf(stderr, "pa: %d from buffer\n", nBytesLeftInBuffer);
 	    }
@@ -402,33 +415,33 @@
 	    readOffset += nBytesLeftInBuffer;
 	}
     }
-    if (paBuffer != NULL) {
-	paStreamData->readOffset = readOffset;
-	if (readOffset >= paBuffer->bufferSize) {
-	    LOCK(paStreamData->lock);
+    if (buffer != NULL) {
+	streamData->readOffset = readOffset;
+	if (readOffset >= buffer->bufferSize) {
+	    LOCK(streamData->lock);
 	    // next buffer
-	    paStreamData->currentBuffer = paBuffer->nextBuffer;
-	    paStreamData->readOffset = readOffset = 0;
+	    streamData->currentBuffer = buffer->nextBuffer;
+	    streamData->readOffset = readOffset = 0;
 	    // this buffer onto freeList
-	    paBuffer->nextBuffer = paStreamData->freeList;
-	    paStreamData->freeList = paBuffer;
+	    buffer->nextBuffer = streamData->freeList;
+	    streamData->freeList = buffer;
 	    if (DEBUGGING) {
-		fprintf(stderr, "pa: put on freelist: %p\n", paBuffer);
+		fprintf(stderr, "pa: put on freelist: %p\n", buffer);
 	    }
-	    if (paStreamData->currentBuffer == NULL) {
+	    if (streamData->currentBuffer == NULL) {
 		if (DEBUGGING) {
 		    fprintf(stderr, "pa: done with last buffer\n");
 		}
-		paStreamData->lastBuffer = NULL;
+		streamData->lastBuffer = NULL;
 		retVal = paComplete;
 	    }
-	    UNLOCK(paStreamData->lock);
+	    UNLOCK(streamData->lock);
 	    if (nBytesToOutput > 0) {
 		writeOffset += nSent;
-		if (paStreamData->currentBuffer == NULL) {
+		if (streamData->currentBuffer == NULL) {
 		    memset(outputBuffer+writeOffset, 0, nBytesToOutput);
 		} else {
-		    paBuffer = paStreamData->currentBuffer;
+		    buffer = streamData->currentBuffer;
 		    readOffset = 0;
 		    if (DEBUGGING) {
 			fprintf(stderr, "pa: %d more\n", nBytesToOutput);
@@ -448,9 +461,9 @@
 static void
 paStreamFinished( void* userData )
 {
-    struct paStreamData *paStreamData = (struct paStreamData *)userData;
-
-    paStreamData->hasFinished = 1;
+    struct paStreamData *streamData = (struct paStreamData *)userData;
+
+    streamData->hasFinished = 1;
     if (DEBUGGING) {
 	fprintf(stderr, "pa: Stream Completed\n" );
     }
@@ -458,6 +471,132 @@
 
 #endif /* SUPPORT_PORTAUDIO */
 
+#ifdef SUPPORT_JACKAUDIO
+
+struct jackBuffer {
+    void* sampleData;
+    int bufferSize;
+    struct jackBuffer* nextBuffer;
+};
+
+struct jackStreamData {
+    jack_port_t *jack_input_port;
+    jack_port_t *jack_output_port;
+    jack_client_t *jack_client;
+    MUTEX lock;
+    int readOffset;
+    int bytesPerSample;     // bytesPerSample (1, 2 or 4)
+    int nChannels;          // num channels (1 or 2)
+    int hasFinished;
+    struct jackBuffer* currentBuffer;
+    struct jackBuffer* lastBuffer;
+    struct jackBuffer* freeList;
+};
+
+/*
+ * This routine will be called by the Jack engine when audio is needed.
+ * It may called at interrupt level on some machines, so don't do anything
+ * that could mess up the system like calling malloc() or free().
+ */
+static int
+jack_callback(jack_nframes_t nframes, void *userData)
+{
+    struct jackStreamData *streamData = (struct jackStreamData*)userData;
+    struct jackBuffer *buffer = streamData->currentBuffer;
+    int nBytesToOutput = streamData->bytesPerSample * streamData->nChannels * nframes;
+    // int nBytesToOutput = sizeof (jack_default_audio_sample_t) * nframes;
+    int nBytesLeftInBuffer;
+    int readOffset;
+    int writeOffset = 0;
+    int nSent;
+    int retVal = paContinue;
+    jack_default_audio_sample_t *in, *out;
+
+    in = jack_port_get_buffer (streamData->jack_input_port, nframes);
+    out = jack_port_get_buffer (streamData->jack_output_port, nframes);
+
+    readOffset = streamData->readOffset;
+  again:
+    if (buffer != NULL) {
+	nBytesLeftInBuffer = buffer->bufferSize - readOffset;
+	if (DEBUGGING) {
+	    fprintf(stderr, "jack: %d from %p[%d] to [%d]; next: %p last: %p)\n",
+			nBytesLeftInBuffer, buffer, readOffset, writeOffset,
+			buffer->nextBuffer, streamData->lastBuffer);
+	}
+    } else {
+	nBytesLeftInBuffer = 0;
+    }
+    if (nBytesLeftInBuffer >= nBytesToOutput) {
+	memcpy(out+writeOffset, buffer->sampleData+readOffset, nBytesToOutput);
+	nSent = nBytesToOutput;
+	if (DEBUGGING) {
+	    fprintf(stderr, "jack: %d from buffer\n", nSent);
+	}
+	nBytesToOutput = 0;
+	readOffset += nSent;
+    } else {
+	nSent = nBytesLeftInBuffer;
+	if (nSent > 0) {
+	    memcpy(out+writeOffset, buffer->sampleData+readOffset, nBytesLeftInBuffer);
+	    if (DEBUGGING) {
+		fprintf(stderr, "jack: %d from buffer\n", nBytesLeftInBuffer);
+	    }
+	    nBytesToOutput -= nSent;
+	    readOffset += nBytesLeftInBuffer;
+	}
+    }
+    if (buffer != NULL) {
+	streamData->readOffset = readOffset;
+	if (readOffset >= buffer->bufferSize) {
+	    LOCK(streamData->lock);
+	    // next buffer
+	    streamData->currentBuffer = buffer->nextBuffer;
+	    streamData->readOffset = readOffset = 0;
+	    // this buffer onto freeList
+	    buffer->nextBuffer = streamData->freeList;
+	    streamData->freeList = buffer;
+	    if (DEBUGGING) {
+		fprintf(stderr, "jack: put on freelist: %p\n", buffer);
+	    }
+	    if (streamData->currentBuffer == NULL) {
+		if (DEBUGGING) {
+		    fprintf(stderr, "jack: done with last buffer\n");
+		}
+		streamData->lastBuffer = NULL;
+		retVal = paComplete;
+	    }
+	    UNLOCK(streamData->lock);
+	    if (nBytesToOutput > 0) {
+		writeOffset += nSent;
+		if (streamData->currentBuffer == NULL) {
+		    memset(out+writeOffset, 0, nBytesToOutput);
+		} else {
+		    buffer = streamData->currentBuffer;
+		    readOffset = 0;
+		    if (DEBUGGING) {
+			fprintf(stderr, "jack: %d more\n", nBytesToOutput);
+		    }
+		    goto again;
+		}
+	    }
+	}
+    }
+    return 0;
+}
+
+/**
+ * JACK calls this shutdown_callback if the server ever shuts down or
+ * decides to disconnect the client.
+ */
+void
+jack_shutdown (void *userData)
+{
+    struct jackStreamData *streamData = (struct jackStreamData*)userData;
+}
+
+#endif /* SUPPORT_JACKAUDIO */
+
 %}
 ! !
 
@@ -516,24 +655,28 @@
 
 determineConcreteClass
     self allSubclassesDo:[:each |
-	each isAbstract ifFalse:[
-	    each isSupported ifTrue:[
-		^ each
-	    ]
-	]
+        each isAbstract ifFalse:[
+            each isSupported ifTrue:[
+                ^ each
+            ]
+        ]
     ].
     ^ nil
+
+    "
+     self determineConcreteClass
+    "
 !
 
 new
     self == SoundStream ifTrue:[
-	ConcreteClass isNil ifTrue:[
-	    ConcreteClass := self determineConcreteClass.
-	    ConcreteClass isNil ifTrue:[
-		self openErrorSignal raiseErrorString:'missing sound support'.
-	    ].
-	].
-	^ ConcreteClass new.
+        ConcreteClass isNil ifTrue:[
+            ConcreteClass := self determineConcreteClass.
+            ConcreteClass isNil ifTrue:[
+                OpenError raiseErrorString:'missing sound support'.
+            ].
+        ].
+        ^ ConcreteClass new.
     ].
     ^ self basicNew initialize
 
@@ -1128,36 +1271,53 @@
 
 !SoundStream class methodsFor:'queries'!
 
+defaultClass
+    ^ ConcreteClass
+
+    "
+     SoundStream defaultClass:(SoundStream::JackAudio)
+    "
+!
+
+defaultClass:aClass
+    self assert:(aClass isSupported).
+    ConcreteClass := aClass.
+
+    "
+     SoundStream defaultClass:(SoundStream::JackAudio)
+    "
+!
+
 isSupported
     ^ false.
 !
 
 noteToHertz:noteName
     "notename is one of:
-        a1, b2, f3, c#3 etc."
+	a1, b2, f3, c#3 etc."
 
     |octave lastDigit note f sharp|
 
     octave := 4.
     (lastDigit := noteName last) isDigit ifTrue:[
-        octave := lastDigit digitValue
+	octave := lastDigit digitValue
     ].
     note := noteName first.
     sharp := (noteName size > 1) and:[noteName second == $#].
     note = $a ifTrue:[
-        f := sharp ifTrue:[466.2] ifFalse:[440]
+	f := sharp ifTrue:[466.2] ifFalse:[440]
     ] ifFalse:[ note = $b ifTrue:[
-        f := 493.8
+	f := 493.8
     ] ifFalse:[ note = $c ifTrue:[
-        f := sharp ifTrue:[277.2] ifFalse:[261.6]
+	f := sharp ifTrue:[277.2] ifFalse:[261.6]
     ] ifFalse:[ note = $d ifTrue:[
-        f := sharp ifTrue:[311.1] ifFalse:[293.6]
+	f := sharp ifTrue:[311.1] ifFalse:[293.6]
     ] ifFalse:[ note = $e ifTrue:[
-        f := 329.6
+	f := 329.6
     ] ifFalse:[ note = $f ifTrue:[
-        f := sharp ifTrue:[370] ifFalse:[349.2]
+	f := sharp ifTrue:[370] ifFalse:[349.2]
     ] ifFalse:[ note = $g ifTrue:[
-        f := sharp ifTrue:[415.3] ifFalse:[392]
+	f := sharp ifTrue:[415.3] ifFalse:[392]
     ]]]]]]].
 
     f := f * (2 raisedTo:octave-4).
@@ -1165,10 +1325,10 @@
 
     "
     SoundStream noteToHertz:'a'
-    SoundStream noteToHertz:'g4' 
-    SoundStream noteToHertz:'e5'  
-    SoundStream noteToHertz:'b3'  
-    SoundStream noteToHertz:'c#3'  
+    SoundStream noteToHertz:'g4'
+    SoundStream noteToHertz:'e5'
+    SoundStream noteToHertz:'b3'
+    SoundStream noteToHertz:'c#3'
     "
 
     "Modified: / 31.1.1999 / 12:06:27 / cg"
@@ -1521,21 +1681,21 @@
     "allocate memory for 1 sec playing time"
     numSamples := self sampleRate.
     audioFormat == #U16 ifTrue:[
-        buffer := WordArray new:numSamples withAll:16r8000.
+	buffer := WordArray new:numSamples withAll:16r8000.
     ] ifFalse:[ audioFormat == #S16 ifTrue:[
-        buffer := SignedWordArray new:numSamples withAll:0.
+	buffer := SignedWordArray new:numSamples withAll:0.
     ] ifFalse:[ audioFormat == #F32 ifTrue:[
-        buffer := FloatArray new:numSamples withAll:0.0.
-    ] ifFalse:[ 
-        self halt
+	buffer := FloatArray new:numSamples withAll:0.0.
+    ] ifFalse:[
+	self halt
     ]]].
 
     1 to:nSeconds truncated do:[:s |
-        self nextPutBytes:(numSamples*2) from:buffer startingAt:1
+	self nextPutBytes:(numSamples*2) from:buffer startingAt:1
     ].
     restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated.
     restSamples > 0 ifTrue:[
-        self nextPutBytes:(restSamples*2) from:buffer startingAt:1
+	self nextPutBytes:(restSamples*2) from:buffer startingAt:1
     ].
 !
 
@@ -1671,8 +1831,8 @@
       g 0.5
       g 0.5
     ) pairWiseDo:[:note :duration |
-        self playSine:(self class noteToHertz:note) forSeconds:duration.
-        self pause:0.05.
+	self playSine:(self class noteToHertz:note) forSeconds:duration.
+	self pause:0.05.
     ].
 
     "
@@ -1685,11 +1845,11 @@
 
 testOctaves
     3 timesRepeat:[
-        self playSine:220 forSeconds:0.5.
-        self playSine:440 forSeconds:0.5.
-        self playSine:880 forSeconds:0.5.
-        self playSine:1760 forSeconds:0.5.
-        self playSine:3520 forSeconds:0.5.
+	self playSine:220 forSeconds:0.5.
+	self playSine:440 forSeconds:0.5.
+	self playSine:880 forSeconds:0.5.
+	self playSine:1760 forSeconds:0.5.
+	self playSine:3520 forSeconds:0.5.
     ]
 
     "
@@ -1726,18 +1886,18 @@
     |buffer numSamples val scale restSamples|
 
     (audioFormat startsWith:#U16) ifFalse:[
-        (audioFormat startsWith:#S16) ifFalse:[
-            self error:'must be in 16bit mode' mayProceed:true.
-            ^ self
-        ]
+	(audioFormat startsWith:#S16) ifFalse:[
+	    self error:'must be in 16bit mode' mayProceed:true.
+	    ^ self
+	]
     ].
 
     "allocate memory for 1 sec playing time"
     numSamples := self sampleRate.
     audioFormat == #U16 ifTrue:[
-        buffer := WordArray new:numSamples.
+	buffer := WordArray new:numSamples.
     ] ifFalse:[
-        buffer := SignedWordArray new:numSamples.
+	buffer := SignedWordArray new:numSamples.
     ].
 
     "fill it with a sine wave"
@@ -1746,20 +1906,20 @@
     "/ each of which is 0 .. 2pi
     scale := freq * 2 * (Float pi) / numSamples.
     1 to:numSamples do:[:i |
-        val := (scale * (i-1)) sin.
-        val := (val * 16r7FFF) rounded.
-        audioFormat == #U16 ifTrue:[
-            val := val + 16r8000
-        ].
-        buffer at:i put:val
+	val := (scale * (i-1)) sin.
+	val := (val * 16r7FFF) rounded.
+	audioFormat == #U16 ifTrue:[
+	    val := val + 16r8000
+	].
+	buffer at:i put:val
     ].
 
     1 to:nSeconds truncated do:[:s |
-        self nextPutBytes:(numSamples*2) from:buffer startingAt:1
+	self nextPutBytes:(numSamples*2) from:buffer startingAt:1
     ].
     restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated.
     restSamples > 0 ifTrue:[
-        self nextPutBytes:(restSamples*2) from:buffer startingAt:1
+	self nextPutBytes:(restSamples*2) from:buffer startingAt:1
     ].
 
     "of course, the frequency should be below half the
@@ -1924,8 +2084,8 @@
     |buffer numSamples val scale restSamples|
 
     (audioFormat == #F32) ifFalse:[
-        self error:'must be in f32 mode' mayProceed:true.
-        ^ self
+	self error:'must be in f32 mode' mayProceed:true.
+	^ self
     ].
 
     "allocate memory for 1sec playing time"
@@ -1936,16 +2096,16 @@
 
     scale := freq * 2 * (Float pi) / numSamples.
     1 to:numSamples do:[:i |
-        val := (scale * i) sin.
-        buffer at:i put:val
+	val := (scale * i) sin.
+	buffer at:i put:val
     ].
 
     1 to:nSeconds truncated do:[:s |
-        self nextPutBytes:(numSamples*4) from:buffer startingAt:1
+	self nextPutBytes:(numSamples*4) from:buffer startingAt:1
     ].
     restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated.
     restSamples > 0 ifTrue:[
-        self nextPutBytes:(restSamples*4) from:buffer startingAt:1
+	self nextPutBytes:(restSamples*4) from:buffer startingAt:1
     ].
 
     "of course, the frequency should be below half the
@@ -3301,6 +3461,162 @@
     self errorUnsupportedOperation
 ! !
 
+!SoundStream::JackAudio class methodsFor:'queries'!
+
+isSupported
+%{
+#ifdef SUPPORT_JACKAUDIO
+    RETURN(true);
+#endif
+%}.
+    ^ false.
+! !
+
+!SoundStream::JackAudio methodsFor:'open & close'!
+
+closeFile
+    "a stream has been collected - close the file"
+
+%{
+#ifdef SUPPORT_JACKAUDIO
+    OBJ str;
+    if ((str = __INST(handle1)) != nil) {
+	struct jackStreamData* streamData = (struct jackStreamData*)__externalAddressVal(str);
+	struct jackBuffer* buffer;
+
+	if (DEBUGGING) {
+	    fprintf(stderr, "jack close\n");
+	}
+	__externalAddressVal(str) = NULL;
+	__INST(handle1) = nil;
+	jack_client_close( streamData->jack_client );
+
+	LOCK(streamData->lock);
+
+	buffer = streamData->currentBuffer;
+	streamData->currentBuffer = NULL;
+	streamData->lastBuffer = NULL;
+	while (buffer != NULL) {
+	    struct jackBuffer* nextBuffer = buffer->nextBuffer;
+	    free(buffer->sampleData);
+	    free(buffer);
+	    buffer = nextBuffer;
+	}
+	buffer = streamData->freeList;
+	streamData->freeList = NULL;
+	while (buffer != NULL) {
+	    struct jackBuffer* nextBuffer = buffer->nextBuffer;
+	    free(buffer->sampleData);
+	    free(buffer);
+	    buffer = nextBuffer;
+	}
+
+	UNLOCK(streamData->lock);
+	RELEASELOCK(streamData->lock);
+	free(streamData);
+    }
+    RETURN (self);
+#endif // SUPPORT_JACKAUDIO
+%}.
+!
+
+openWithMode:aMode attributes:attributeSpec
+    |ok error errorStringOrNil|
+
+    openMode := aMode.
+    openAttributes := attributeSpec.
+%{
+#ifdef SUPPORT_JACKAUDIO
+    jack_options_t options = JackNullOption;
+    jack_client_t *client;
+    struct jackStreamData* streamData;
+    int nChannels, sampleRate, bytesPerSample;
+#   define FRAMES_PER_BUFFER 128
+
+    ok = false;
+
+    if (__isSmallInteger(__INST(numberOfChannels))) {
+	nChannels = __intVal(__INST(numberOfChannels));
+    } else {
+	nChannels = 1;
+    }
+
+    if (__INST(audioFormat) == @symbol(F32)) {
+	bytesPerSample = 4;
+    } else {
+	fprintf(stderr, "SoundStream [warning]: only f32 supported with jack\n");
+	errorStringOrNil = __MKSTRING("audioFormat must be f32 with jack");
+	goto out;
+    }
+
+    if (__isSmallInteger(__INST(sampleRate))) {
+	sampleRate = __intVal(__INST(sampleRate));
+    } else {
+	fprintf(stderr, "SoundStream [warning]: using default sampleRate 8000\n");
+	sampleRate = 8000;
+    }
+
+    streamData = (struct jackStreamData*)malloc(sizeof(struct jackStreamData));
+    if (streamData == NULL) {
+	fprintf(stderr, "SoundStream [warning]: failed to allocate jackStream\n");
+	errorStringOrNil = __MKSTRING("failed to allocate jackStream");
+	goto out;
+    }
+
+    if ((client = jack_client_open ("stx", JackNullOption, NULL)) == 0) {
+	fprintf(stderr, "SoundStream [warning]: client_open\n");
+	free(streamData);
+	errorStringOrNil = __MKSTRING("client_open");
+	goto out;
+    }
+
+    streamData->jack_client = client;
+    INITLOCK(streamData->lock);
+    streamData->readOffset = 0;
+    streamData->bytesPerSample = bytesPerSample;
+    streamData->nChannels = nChannels;
+
+    streamData->currentBuffer = NULL;
+    streamData->lastBuffer = NULL;
+    streamData->freeList = NULL;
+    streamData->hasFinished = 0;
+
+    {
+	OBJ t;
+	t = __MKEXTERNALADDRESS(streamData); __INST(handle1) = t; __STORE(self, t);
+    }
+    __INST(binary) = true;
+
+//    paErr = Pa_SetStreamFinishedCallback( stream, &paStreamFinished );
+//    if( paErr != paNoError ) {
+//        fprintf(stderr, "SoundStream [warning]: setFinishedCallback: %s\n", Pa_GetErrorText( paErr ));
+//        free(paStreamData);
+//        errorStringOrNil = __MKSTRING(Pa_GetErrorText( paErr ));
+//        goto out;
+//    };
+
+    ok = true;
+out:;
+#endif /* SUPPORT_JACKAUDIO */
+
+%}.
+    ok == false ifTrue:[
+	lastErrorString := errorStringOrNil.
+	lastErrorNumber := error ? -1.
+	self openError:error.
+	"normally not reached"
+	^ nil.
+    ].
+    self registerForFinalization.
+
+!
+
+reopenStream
+    handle1 isNil ifTrue:[^ self].
+    self closeFile.
+    self openWithMode:openMode attributes:openAttributes
+! !
+
 !SoundStream::PortAudio class methodsFor:'default values'!
 
 defaultSampleRate
@@ -3392,8 +3708,8 @@
     "wait until all sound has been played"
 
     [self hasOutputPending] whileTrue:[
-        Delay waitForMilliseconds:20.
-        "/ Processor yield
+	Delay waitForMilliseconds:20.
+	"/ Processor yield
     ]
 !
 
@@ -3402,9 +3718,9 @@
 #ifdef SUPPORT_PORTAUDIO
     OBJ str;
     if ((str = __INST(handle1)) != nil) {
-        struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
-
-        RETURN ((paStreamData->currentBuffer != NULL) ? true : false);
+	struct paStreamData* streamData = (struct paStreamData*)__externalAddressVal(str);
+
+	RETURN ((streamData->currentBuffer != NULL) ? true : false);
     }
 #endif /* SUPPORT_PORTAUDIO */
 %}.
@@ -3420,7 +3736,7 @@
 #ifdef SUPPORT_PORTAUDIO
     OBJ str;
     if ((str = __INST(handle1)) != nil) {
-	struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
+	struct paStreamData* streamData = (struct paStreamData*)__externalAddressVal(str);
 	struct paBuffer* buffer;
 
 	if (DEBUGGING) {
@@ -3428,22 +3744,22 @@
 	}
 	__externalAddressVal(str) = NULL;
 	__INST(handle1) = nil;
-	// Pa_StopStream( paStreamData->stream );
-	Pa_CloseStream( paStreamData->stream );
-
-	LOCK(paStreamData->lock);
-
-	buffer = paStreamData->currentBuffer;
-	paStreamData->currentBuffer = NULL;
-	paStreamData->lastBuffer = NULL;
+	// Pa_StopStream( streamData->stream );
+	Pa_CloseStream( streamData->stream );
+
+	LOCK(streamData->lock);
+
+	buffer = streamData->currentBuffer;
+	streamData->currentBuffer = NULL;
+	streamData->lastBuffer = NULL;
 	while (buffer != NULL) {
 	    struct paBuffer* nextBuffer = buffer->nextBuffer;
 	    free(buffer->sampleData);
 	    free(buffer);
 	    buffer = nextBuffer;
 	}
-	buffer = paStreamData->freeList;
-	paStreamData->freeList = NULL;
+	buffer = streamData->freeList;
+	streamData->freeList = NULL;
 	while (buffer != NULL) {
 	    struct paBuffer* nextBuffer = buffer->nextBuffer;
 	    free(buffer->sampleData);
@@ -3451,9 +3767,9 @@
 	    buffer = nextBuffer;
 	}
 
-	UNLOCK(paStreamData->lock);
-	RELEASELOCK(paStreamData->lock);
-	free(paStreamData);
+	UNLOCK(streamData->lock);
+	RELEASELOCK(streamData->lock);
+	free(streamData);
     }
     RETURN (self);
 #endif // SUPPORT_PORTAUDIO
@@ -3470,7 +3786,7 @@
     static PaStreamParameters outputParameters;
     PaStream *stream;
     PaError paErr;
-    struct paStreamData* paStreamData;
+    struct paStreamData* streamData;
     int nChannels, sampleRate, bytesPerSample;
 #   define FRAMES_PER_BUFFER 128
 
@@ -3530,8 +3846,8 @@
 	sampleRate = 8000;
     }
 
-    paStreamData = (struct paStreamData*)malloc(sizeof(struct paStreamData));
-    if (paStreamData == NULL) {
+    streamData = (struct paStreamData*)malloc(sizeof(struct paStreamData));
+    if (streamData == NULL) {
 	fprintf(stderr, "SoundStream [warning]: failed to allocate paStream\n");
 	errorStringOrNil = __MKSTRING("failed to allocate paStream");
 	goto out;
@@ -3545,35 +3861,35 @@
 	      FRAMES_PER_BUFFER,
 	      paClipOff,      /* we won't output out of range samples so don't bother clipping them */
 	      paCallback,
-	      paStreamData );
+	      streamData );
 
     if (paErr != paNoError) {
 	fprintf(stderr, "SoundStream [warning]: openStream: %s\n", Pa_GetErrorText( paErr ));
-	free(paStreamData);
+	free(streamData);
 	errorStringOrNil = __MKSTRING(Pa_GetErrorText( paErr ));
 	goto out;
     }
-    paStreamData->stream = stream;
-    INITLOCK(paStreamData->lock);
-    paStreamData->readOffset = 0;
-    paStreamData->bytesPerSample = bytesPerSample;
-    paStreamData->nChannels = nChannels;
-
-    paStreamData->currentBuffer = NULL;
-    paStreamData->lastBuffer = NULL;
-    paStreamData->freeList = NULL;
-    paStreamData->hasFinished = 0;
+    streamData->stream = stream;
+    INITLOCK(streamData->lock);
+    streamData->readOffset = 0;
+    streamData->bytesPerSample = bytesPerSample;
+    streamData->nChannels = nChannels;
+
+    streamData->currentBuffer = NULL;
+    streamData->lastBuffer = NULL;
+    streamData->freeList = NULL;
+    streamData->hasFinished = 0;
 
     {
 	OBJ t;
-	t = __MKEXTERNALADDRESS(paStreamData); __INST(handle1) = t; __STORE(self, t);
+	t = __MKEXTERNALADDRESS(streamData); __INST(handle1) = t; __STORE(self, t);
     }
     __INST(binary) = true;
 
     paErr = Pa_SetStreamFinishedCallback( stream, &paStreamFinished );
     if( paErr != paNoError ) {
 	fprintf(stderr, "SoundStream [warning]: setFinishedCallback: %s\n", Pa_GetErrorText( paErr ));
-	free(paStreamData);
+	free(streamData);
 	errorStringOrNil = __MKSTRING(Pa_GetErrorText( paErr ));
 	goto out;
     };
@@ -3699,7 +4015,7 @@
 	if (__INST(mode) != @symbol(readonly)) {
 	    if (__bothSmallInteger(count, start)) {
 		// allocate a buffer
-		struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
+		struct paStreamData* streamData = (struct paStreamData*)__externalAddressVal(str);
 		int cnt = __intVal(count);
 		int offs = __intVal(start) - 1;
 		int objSize;
@@ -3712,12 +4028,12 @@
 		    unsigned char* newSampleData;
 
 		    // try freeList
-		    LOCK(paStreamData->lock);
-		    if ((paStreamData->freeList != NULL)
-		     && (paStreamData->freeList->bufferSize == cnt)) {
+		    LOCK(streamData->lock);
+		    if ((streamData->freeList != NULL)
+		     && (streamData->freeList->bufferSize == cnt)) {
 			// reuse
-			newBuffer = paStreamData->freeList;
-			paStreamData->freeList = newBuffer->nextBuffer;
+			newBuffer = streamData->freeList;
+			streamData->freeList = newBuffer->nextBuffer;
 			newSampleData = newBuffer->sampleData;
 			toFree = NULL;
 			if (DEBUGGING) {
@@ -3725,8 +4041,8 @@
 			}
 		    } else {
 			// free them all
-			toFree = paStreamData->freeList;
-			paStreamData->freeList = NULL;
+			toFree = streamData->freeList;
+			streamData->freeList = NULL;
 			newBuffer = malloc(sizeof(struct paBuffer));
 			newSampleData = malloc(cnt);
 			newBuffer->sampleData = newSampleData;
@@ -3739,20 +4055,20 @@
 		    memcpy(newSampleData, (__ByteArrayInstPtr(anObject)->ba_element)+offs, cnt);
 		    newBuffer->nextBuffer = NULL;
 
-		    if (paStreamData->lastBuffer == NULL) {
+		    if (streamData->lastBuffer == NULL) {
 			// start stream's buffer list
-			paStreamData->currentBuffer = newBuffer;
+			streamData->currentBuffer = newBuffer;
 			mustStart = 1;
 		    } else {
 			// append to stream's buffer list
-			paStreamData->lastBuffer->nextBuffer = newBuffer;
+			streamData->lastBuffer->nextBuffer = newBuffer;
 		    }
-		    paStreamData->lastBuffer = newBuffer;
-		    paStreamData->hasFinished = 0;
-		    UNLOCK(paStreamData->lock);
+		    streamData->lastBuffer = newBuffer;
+		    streamData->hasFinished = 0;
+		    UNLOCK(streamData->lock);
 
 		    if (mustStart) {
-			PaError paErr = Pa_StartStream( paStreamData->stream );
+			PaError paErr = Pa_StartStream( streamData->stream );
 			if ( paErr != paNoError ) {
 			    if (DEBUGGING) {
 				fprintf(stderr, "start error\n");