--- a/SoundStream.st Thu Jan 02 00:50:20 2020 +0100
+++ b/SoundStream.st Fri Jan 03 14:51:21 2020 +0100
@@ -18,11 +18,46 @@
FileStream subclass:#SoundStream
instanceVariableNames:'sampleRate numberOfChannels bitsPerSample audioFormat handle1
handle2 bufferOffset bufferSize lastErrorString fragmentSize'
- classVariableNames:'IsInitialized UnsupportedOperationSignal'
+ classVariableNames:'IsInitialized UnsupportedOperationSignal ConcreteClass'
poolDictionaries:''
category:'Streams-External'
!
+SoundStream subclass:#DevAudio
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:SoundStream
+!
+
+SoundStream subclass:#IRISAudio
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:SoundStream
+!
+
+SoundStream subclass:#PortAudio
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:SoundStream
+!
+
+SoundStream subclass:#Win32DirectSound
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:SoundStream
+!
+
+SoundStream subclass:#Win32WaveSound
+ instanceVariableNames:''
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:SoundStream
+!
+
!SoundStream primitiveDefinitions!
%{
@@ -450,37 +485,43 @@
UnsupportedOperationSignal nameClass:self message:#unsupportedOperationSignal.
UnsupportedOperationSignal notifierString:'unsupported operation'.
]
-!
-
-primitiveInitializeDevice
- |errorMessageOrNil|
-
-%{
-#ifdef USE_PORTAUDIO
- {
- PaError paErr = 0;
-
- if (DEBUGGING) {
- fprintf(stderr, "calling Pa_Initialize...\n");
- }
- if ((paErr = Pa_Initialize()) != paNoError ) {
- errorMessageOrNil = __MKSTRING(Pa_GetErrorText( paErr ) );
- fprintf(stderr, "SoundStream [error]: Pa_Initialize failed\n");
- };
- }
-#endif
-%}.
- errorMessageOrNil notNil ifTrue:[
- self error:'failed to initialize audio device: ',errorMessageOrNil
- ].
! !
!SoundStream class methodsFor:'instance creation'!
+determineConcreteClass
+ self allSubclassesDo:[:each |
+ each isAbstract ifFalse:[
+ each isSupported ifTrue:[
+ ^ each
+ ]
+ ]
+ ].
+ ^ nil
+!
+
+new
+ self == SoundStream ifTrue:[
+ ConcreteClass isNil ifTrue:[
+ ConcreteClass := self determineConcreteClass.
+ ConcreteClass isNil ifTrue:[
+ self openErrorSignal raiseErrorString:'missing sound support'.
+ ].
+ ].
+ ^ ConcreteClass new.
+ ].
+ ^ self basicNew initialize
+
+ "
+ SoundStream new
+ "
+!
+
readWrite
"create and return a new soundStream for readWrite (i.e. filtering)"
|newStream|
+
newStream := (self basicNew) initialize.
newStream openForReadWrite isNil ifTrue:[^nil].
^ newStream
@@ -1005,15 +1046,6 @@
defaultSampleRate
"minimum, supported by all audio systems"
-%{
-#ifdef USE_DEV_AUDIO
- RETURN (__MKSMALLINT(DEV_AUDIO_DEFAULT_FREQ));
-#endif
-#ifdef USE_PORTAUDIO
- RETURN (__MKSMALLINT(PORTAUDIO_DEFAULT_FREQ));
-#endif
-%}.
-
^ 8000
! !
@@ -1022,8 +1054,13 @@
playSoundFile:aFilename
"play a soundFile"
- |inStream soundStream count totalCount buffer startTime playTime delayedTime waitTime|
-
+ |mime inStream soundStream count totalCount buffer startTime playTime delayedTime waitTime|
+
+ mime := aFilename asFilename mimeTypeFromName.
+ mime isAudioType ifFalse:[
+ self error:'not an audio file: ',aFilename asFilename baseName
+ ].
+ self halt.
inStream := aFilename asFilename readStream.
inStream isNil ifTrue:[self error:'cannot open'].
@@ -1035,8 +1072,8 @@
buffer := ByteArray new:4096.
[(count := inStream nextBytesInto:buffer) > 0] whileTrue:[
- totalCount := totalCount + count.
- soundStream nextPutBytes:count from:buffer.
+ totalCount := totalCount + count.
+ soundStream nextPutBytes:count from:buffer.
].
inStream close.
@@ -1056,6 +1093,7 @@
"
SoundStream playSoundFile:'/usr/local/lib/sounds/laugh.snd'
SoundStream playSoundFile:'/usr/local/lib/sounds/spacemusic.snd'
+ SoundStream playSoundFile:'../../goodies/not_ported/sound/sampleFiles/pluck-pcm16.aiff'
"
"Created: / 17-11-1995 / 17:25:30 / cg"
@@ -1064,6 +1102,10 @@
!SoundStream class methodsFor:'queries'!
+isSupported
+ ^ false.
+!
+
usedAudio
"returns a symbol describing which audio system is used;
one of PORTAUDIO, DEV_AUDIO, WIN32_WAVESOUND, IRIS_AUDIO"
@@ -1189,196 +1231,24 @@
dumpSettings
"debugging interface - dump the current settings"
- |fd blockSize speed channels stereo format|
-
- fd := self fileDescriptorOrNil.
- fd isNil ifTrue:[
- self errorNotOpen.
- ^ nil
- ].
audioFormat == #S16 ifTrue:[
- UninterpretedBytes isBigEndian ifTrue:[
- audioFormat := #S16_BE
- ] ifFalse:[
- audioFormat := #S16_LE
- ]
+ UninterpretedBytes isBigEndian ifTrue:[
+ audioFormat := #S16_BE
+ ] ifFalse:[
+ audioFormat := #S16_LE
+ ]
].
audioFormat == #U16 ifTrue:[
- UninterpretedBytes isBigEndian ifTrue:[
- audioFormat := #U16_BE
- ] ifFalse:[
- audioFormat := #U16_LE
- ]
+ UninterpretedBytes isBigEndian ifTrue:[
+ audioFormat := #U16_BE
+ ] ifFalse:[
+ audioFormat := #U16_LE
+ ]
].
-
-%{
- int f = __intVal(fd);
- int __blockSize = -1;
- int __speed = -1;
- int __channels = __intVal(__INST(numberOfChannels));
- int __stereo = __channels > 1;
- int __format = -1;
-
- if (0) {
- }
- else if (__INST(audioFormat) == @symbol(MU_LAW)) {
-# ifdef AFMT_MU_LAW
- __format = AFMT_MU_LAW;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(A_LAW)) {
-# ifdef AFMT_A_LAW
- __format = AFMT_A_LAW;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(IMA_ADPCM)) {
-# ifdef AFMT_IMA_ADPCM
- __format = AFMT_IMA_ADPCM;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(U8)) {
-# ifdef AFMT_U8
- __format = AFMT_U8;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(S16_LE)) {
-# ifdef AFMT_S16_LE
- __format = AFMT_S16_LE;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(S16_BE)) {
-# ifdef AFMT_S16_BE
- __format = AFMT_S16_BE;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(S8)) {
-# ifdef AFMT_S8
- __format = AFMT_S8;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(U16_LE)) {
-# ifdef AFMT_U16_LE
- __format = AFMT_U16_LE;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(U16_BE)) {
-# ifdef AFMT_U16_BE
- __format = AFMT_U16_BE;
-# endif
- }
- else if (__INST(audioFormat) == @symbol(MPEG)) {
-# ifdef AFMT_MPEG
- __format = AFMT_MPEG;
-# endif
- }
-
-#ifdef USE_DEV_AUDIO
- channels = nil;
- blockSize = nil;
- stereo = nil;
- speed = nil;
-
-# if defined(SNDCTL_DSP_GETBLKSIZE)
- if (ioctl(f, SNDCTL_DSP_GETBLKSIZE, &__blockSize) >= 0) {
- blockSize = __MKSMALLINT(__blockSize);
- }
-# endif
-# if defined(SNDCTL_DSP_CHANNELS)
- if (ioctl(f, SNDCTL_DSP_CHANNELS, &__channels) >= 0) {
- channels = __MKSMALLINT(__channels);
- stereo = __MKSMALLINT(__channels > 1);
- }
-# else
-# if defined(SNDCTL_DSP_STEREO)
- if (ioctl(f, SNDCTL_DSP_STEREO, &__stereo) >= 0) {
- stereo = __MKSMALLINT(__stereo);
- }
-# endif
-# endif
-# if defined(SNDCTL_DSP_SPEED)
- if (ioctl(f, SNDCTL_DSP_SPEED, &__speed) >= 0) {
- speed = __MKSMALLINT(__speed);
- }
-# endif
-# if defined(SNDCTL_DSP_GETFMT)
- if (ioctl(f, SNDCTL_DSP_GETFMT, &__format) >= 0) {
- format = __MKSMALLINT(__format);
- }
-# else
-# if defined(SNDCTL_DSP_SETFMT) && defined(AFMT_QUERY)
- __format = AFMT_QUERY;
- if (ioctl(f, SNDCTL_DSP_SETFMT, &__format) >= 0) {
- switch (__format) {
-# ifdef AFMT_MU_LAW
- case AFMT_MU_LAW:
- format = @symbol(MU_LAW);
- break;
-# endif
-# ifdef AFMT_A_LAW
- case AFMT_A_LAW:
- format = @symbol(A_LAW);
- break;
-# endif
-# ifdef AFMT_U8
- case AFMT_U8:
- format = @symbol(U8);
- break;
-# endif
-# ifdef AFMT_S8
- case AFMT_S8:
- format = @symbol(S8);
- break;
-# endif
-# ifdef AFMT_S16_LE
- case AFMT_S16_LE:
- format = @symbol(S16_LE);
- break;
-# endif
-# ifdef AFMT_S16_BE
- case AFMT_S16_BE:
- format = @symbol(S16_BE);
- break;
-# endif
-# ifdef AFMT_U16_LE
- case AFMT_U16_LE:
- format = @symbol(U16_LE);
- break;
-# endif
-# ifdef AFMT_U16_BE
- case AFMT_U16_BE:
- format = @symbol(U16_BE);
- break;
-# endif
-# ifdef AFMT_MPEG
- case AFMT_MPEG:
- format = @symbol(MPEG);
- break;
-# endif
- default:
- format = nil;
- }
- }
-# endif
-# endif
-#endif /* USE_DEV_AUDIO */
-
-%}.
- format notNil ifTrue:[
- Transcript show:'format: '; showCR:format
- ].
- blockSize notNil ifTrue:[
- Transcript show:'blockSize: '; showCR:blockSize
- ].
- speed notNil ifTrue:[
- Transcript show:'speed: '; showCR:speed
- ].
- channels notNil ifTrue:[
- Transcript show:'channels: '; showCR:channels
- ].
- stereo notNil ifTrue:[
- Transcript show:'stereo: '; showCR:stereo
- ].
-
+ Transcript show:'sampleRate: '; showCR:sampleRate.
+ Transcript show:'format: '; showCR:audioFormat.
+ Transcript show:'fragmentSize: '; showCR:fragmentSize.
+ Transcript show:'channels: '; showCR:numberOfChannels.
Transcript show:'supported audioFormats: '; showCR:(self supportedAudioFormats).
"
@@ -1406,49 +1276,11 @@
numberOfChannels := self class defaultNumberOfChannels.
sampleRate := self class defaultSampleRate.
pathName := nil.
-
- OperatingSystem isUNIXlike ifTrue:[
- '/dev/audio' asFilename exists ifTrue:[
- "/
- "/ sunos or linux
- "/
- pathName := '/dev/audio'.
- ].
- ].
-
- (IsInitialized ? false) ifFalse:[
- self class primitiveInitializeDevice
- ].
-
- "Created: 17.11.1995 / 17:28:14 / cg"
!
resetSoundCard
"debugging interface - reset the soundCard"
- |fd|
-
- handle notNil ifTrue:[
- fd := self fileDescriptor.
- fd isNil ifTrue:[
- self errorNotOpen.
- ^ nil
- ]
- ].
-%{
- int f = __intVal(fd);
- int __dummy;
-
-#ifdef USE_DEV_AUDIO
- if (__isSmallInteger(fd)) {
-# if defined(SNDCTL_DSP_RESET)
- if (ioctl(f, SNDCTL_DSP_RESET, &__dummy) >= 0) {
- RETURN (self);
- }
-# endif
- }
-#endif /* USE_DEV_AUDIO */
-%}.
^ UnsupportedOperationSignal raise
"
@@ -1460,166 +1292,26 @@
"set the format of the audio data as specified by aSymbol.
Returns true if sucessful - may fail with some formats on many sound devices."
- |fd ok|
-
- handle notNil ifTrue:[
- fd := self fileDescriptor.
- fd isNil ifTrue:[
- self errorNotOpen.
- ^ nil
- ]
- ].
-%{
- OBJ sym = aSymbol;
-
- if (__INST(audioFormat) == sym) {
- RETURN (self);
- }
-
-#ifdef USE_DEV_AUDIO
- if (__isSmallInteger(fd)) {
- int f = __intVal(fd);
- int __fmt = 0, __fmtWant;
- union {
- unsigned short us;
- unsigned char ub[2];
- } u;
-
- if (__isSymbol(sym)) {
- if (sym == @symbol(U16)) {
- u.us = 0x1234;
- if (u.ub[0] == 0x34) {
- /* printf("U16_LE\n"); */
- sym = @symbol(U16_LE);
- } else {
- /* printf("U16_BE\n"); */
- sym = @symbol(U16_BE);
- }
- } else if (sym == @symbol(S16)) {
- u.us = 0x1234;
- if (u.ub[0] == 0x34) {
- /* printf("S16_LE\n"); */
- sym = @symbol(S16_LE);
- } else {
- /* printf("S16_BE\n"); */
- sym = @symbol(S16_BE);
- }
- }
-
- if (0) {
-# ifdef AFMT_MU_LAW
- } else if (sym == @symbol(MU_LAW)) {
- __fmt = AFMT_MU_LAW;
-# endif
-# ifdef AFMT_A_LAW
- } else if (sym == @symbol(A_LAW)) {
- __fmt = AFMT_A_LAW;
-# endif
-# ifdef AFMT_IMA_ADPCM
- } else if (sym == @symbol(IMA_ADPCM)) {
- __fmt = AFMT_IMA_ADPCM;
-# endif
-# ifdef AFMT_U8
- } else if (sym == @symbol(U8)) {
- __fmt = AFMT_U8;
-# endif
-# ifdef AFMT_S8
- } else if (sym == @symbol(S8)) {
- __fmt = AFMT_S8;
-# endif
-# ifdef AFMT_U16_LE
- } else if (sym == @symbol(U16_LE)) {
- __fmt = AFMT_U16_LE;
-# endif
-# ifdef AFMT_U16_BE
- } else if (sym == @symbol(U16_BE)) {
- __fmt = AFMT_U16_BE;
-# endif
-# ifdef AFMT_S16_LE
- } else if (sym == @symbol(S16_LE)) {
- __fmt = AFMT_S16_LE;
-# endif
-# ifdef AFMT_S16_BE
- } else if (sym == @symbol(S16_BE)) {
- __fmt = AFMT_S16_BE;
-# endif
-# ifdef AFMT_MPEG
- } else if (sym == @symbol(MPEG)) {
- __fmt = AFMT_MPEG;
-# endif
-
-# ifdef AUDIO_FORMAT_LINEAR16BIT
- } else if (sym == @symbol(S16)) {
- __fmt = AUDIO_FORMAT_LINEAR16BIT;
-# endif
-# ifdef AUDIO_FORMAT_ULAW
- } else if (sym == @symbol(MU_LAW)) {
- __fmt = AUDIO_FORMAT_ULAW;
-# endif
-# ifdef AUDIO_FORMAT_ALAW
- } else if (sym == @symbol(A_LAW)) {
- __fmt = AUDIO_FORMAT_ALAW;
-# endif
- } else {
- console_fprintf(stderr, "bad format: %s\n", __stringVal(sym));
- ok = false;
- goto bad;
- }
- }
-
-# ifdef SNDCTL_DSP_SETFMT
- __fmtWant = __fmt;
-
- if (ioctl(f, SNDCTL_DSP_SETFMT, &__fmt) >= 0) {
- if (__fmt == __fmtWant) {
- __INST(audioFormat) = sym;
- } else {
- /* console_fprintf(stderr, "want: %x; got: %x\n", __fmtWant, __fmt); */
- }
- } else {
- /* console_fprintf(stderr, "got err-ret from setFmp %x\n", __fmt); */
- ok = false;
- }
-# else
-# ifdef AUDIO_SET_DATA_FORMAT /* hpux */
- if (ioctl (f, AUDIO_SET_DATA_FORMAT, __fmt)) {
- /* console_fprintf(stderr, "got err-ret from AUDIO_SET_DATA_FORMAT\n"); */
- }
-# endif
-# endif /* SNDCTL_DSP_SETFMT */
-
-bad: ;
- }
-#endif /* USE_DEV_AUDIO */
-
-#ifdef USE_PORTAUDIO
- __INST(audioFormat) = aSymbol;
- __STORE(self, aSymbol);
-#endif
-
-%}.
- (ok ? true) ifFalse:[
- ^ UnsupportedOperationSignal raise
- ].
+ audioFormat := aSymbol.
((audioFormat == #U8) or:[audioFormat == #S8]) ifTrue:[
- bitsPerSample := 8.
- ^ self
+ bitsPerSample := 8.
+ ^ self
].
(#(U16 U16_LE U16_BE S16 S16_LE S16_BE) includes:audioFormat) ifTrue:[
- bitsPerSample := 16.
- ^ self
+ bitsPerSample := 16.
+ ^ self
].
(#(U24 U24_LE U24_BE S24 S24_LE S24_BE) includes:audioFormat) ifTrue:[
- bitsPerSample := 24.
- ^ self
+ bitsPerSample := 24.
+ ^ self
].
(#(U32 U32_LE U32_BE S32 S32_LE S32_BE) includes:audioFormat) ifTrue:[
- bitsPerSample := 32.
- ^ self
+ bitsPerSample := 32.
+ ^ self
].
(audioFormat == #F32) ifTrue:[
- bitsPerSample := 32.
- ^ self
+ bitsPerSample := 32.
+ ^ self
].
bitsPerSample := 8.
@@ -1635,61 +1327,7 @@
"set the number of channels (1 -> mono; 2 -> stereo).
Returns true if sucessful - may fail with many sound devices."
- |fd|
-
- handle notNil ifTrue:[
- fd := self fileDescriptor.
- fd isNil ifTrue:[
- self errorNotOpen.
- ^ nil
- ]
- ].
-%{
-
-#ifdef USE_DEV_AUDIO
- if (__isSmallInteger(fd) && __isSmallInteger(nChannels)) {
- int f = __intVal(fd);
- int __nCh = __intVal(nChannels);
-
-# ifdef SNDCTL_DSP_STEREO
- if ((__nCh == 1) || (__nCh == 2)) {
- int __stereo = (__nCh == 2) ? 1 : 0;
-
- if (ioctl(f, SNDCTL_DSP_STEREO, &__stereo) >= 0) {
- if (__stereo == 0) {
- __INST(numberOfChannels) = __MKSMALLINT(1);
- } else {
- __INST(numberOfChannels) = __MKSMALLINT(2);
- }
- RETURN (self);
- }
- }
-# else
-# ifdef SOUND_PCM_WRITE_CHANNELS
- if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &__nCh) >= 0) {
- __INST(numberOfChannels) = nChannels;
- RETURN (self);
- }
-# else
-# ifdef AUDIO_SET_CHANNELS /* hpux */
- if (ioctl (f, AUDIO_SET_CHANNELS, __nCh)) {
- /* console_fprintf(stderr, "got err-ret from AUDIO_SET_CHANNELS\n"); */
- __INST(numberOfChannels) = nChannels;
- RETURN (self);
- }
-# endif
-# endif
-# endif
- }
-#endif /* USE_DEV_AUDIO */
-
-#ifdef USE_PORTAUDIO
- __INST(numberOfChannels) = nChannels;
- RETURN (self);
-#endif
-
-%}.
- ^ UnsupportedOperationSignal raise
+ numberOfChannels := nChannels.
"
self writing setChannels:2; dumpSettings; close
@@ -1699,107 +1337,15 @@
!
setFragmentSize:blockSize
- "set the soundDriver's fragmentSize.
- Returns true if sucessful - may fail with many sound devices."
-
- |fd|
-
- handle notNil ifTrue:[
- fd := self fileDescriptor.
- fd isNil ifTrue:[
- self errorNotOpen.
- ^ nil
- ]
- ].
-%{
- int f = __intVal(fd);
- int __blockSize = 0;
-
-#ifdef USE_DEV_AUDIO
- if (__isSmallInteger(fd) && __isSmallInteger(blockSize)) {
- __blockSize = __intVal(blockSize);
-# if defined(SNDCTL_DSP_SETFRAGMENT)
- if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &__blockSize) >= 0) {
- /* __INST(blockSize) = blockSize; */
- RETURN (self);
- }
-# endif
-# ifdef AUDIO_SET_CHANNELS /* hpux */
- if (ioctl (f, AUDIO_SET_TXBUFSIZE, __blockSize)) {
- /* console_fprintf(stderr, "got err-ret from AUDIO_SET_TXBUFSIZE\n"); */
- /* __INST(blockSize) = blockSize; */
- RETURN (self);
- }
-# endif
- }
-#endif // USE_DEV_AUDIO
-
-#ifdef USE_PORTAUDIO
- __INST(fragmentSize) = blockSize;
- RETURN (self);
-#endif
-%}.
- ^ UnsupportedOperationSignal raise
+ "set the soundDriver's fragmentSize"
+
+ fragmentSize := blockSize.
!
setSampleRate:hz
- "set the sample rate.
- Returns true if sucessful - may fail with many sound devices."
-
- |fd|
-
- handle notNil ifTrue:[
- fd := self fileDescriptor.
- fd isNil ifTrue:[
- self errorNotOpen.
- ^ nil
- ]
- ].
-%{
-#ifdef USE_DEV_AUDIO
- if (__isSmallInteger(fd) && __isSmallInteger(hz)) {
- int f = __intVal(fd);
- int __rate = __intVal(hz);
- int __rateWant = __rate;
-
-# if defined(SNDCTL_DSP_SPEED)
- if (ioctl(f, SNDCTL_DSP_SPEED, &__rate) >= 0) {
- if (__rate != __rateWant) {
- console_fprintf(stderr, "SoundStream [warning]: actual rate is %d\n", __rate);
- hz = __MKSMALLINT(__rate);
- }
- __INST(sampleRate) = hz;
- RETURN (self);
- }
-# else
-# if defined(SOUND_PCM_WRITE_RATE)
- if (ioctl(f, SOUND_PCM_WRITE_RATE, &__rate) >= 0) {
- if (__rate != __rateWant) {
- console_fprintf(stderr, "SoundStream [warning]: actual rate is %d\n", __rate);
- hz = __MKSMALLINT(__rate);
- }
- __INST(sampleRate) = hz;
- RETURN (self);
- }
-# else
-# ifdef AUDIO_SET_SAMPLE_RATE /* hpux */
- if (ioctl (f, AUDIO_SET_SAMPLE_RATE, __rate)) {
- /* console_fprintf(stderr, "got err-ret from AUDIO_SET_SAMPLE_RATE\n"); */
- __INST(sampleRate) = hz;
- RETURN (self);
- }
-# endif
-# endif
-# endif
- }
-#endif // USE_DEV_AUDIO
-
-#ifdef USE_PORTAUDIO
- __INST(sampleRate) = hz;
- RETURN (self);
-#endif
-%}.
- ^ UnsupportedOperationSignal raise
+ "set the sample rate"
+
+ sampleRate := hz.
"
self writing setSampleRate:10000; dumpSettings; close
@@ -1811,185 +1357,29 @@
supportedAudioFormats
"return a collection of supported audio formats.
possibly returned symbols are:
- U8 unsigned 8bit samples
- S8 signed 8bit samples
- U16 unsigned 16bit samples in native format
- U16_LE unsigned 16bit big endian samples
- U16_BE unsigned 16bit big endian samples
- S16 signed 16bit little endian samples in native format
- S16_LE signed 16bit little endian samples
- S16_BE signed 16bit big endian samples
- S24 signed 24bit little endian samples in native format
- S24_LE signed 24bit little endian samples
- S24_BE signed 24bit big endian samples
- S32 signed 32bit little endian samples in native format
- S32_LE signed 32bit little endian samples
- S32_BE signed 32bit big endian samples
- F32 float samples
- MPEG audio mpeg encoded
- MU_LAW u-law encoded 8bit samples
- A_LAW a-law encoded 8bit samples
- IMA_ADPCM adpcm encoded
+ U8 unsigned 8bit samples
+ S8 signed 8bit samples
+ U16 unsigned 16bit samples in native format
+ U16_LE unsigned 16bit big endian samples
+ U16_BE unsigned 16bit big endian samples
+ S16 signed 16bit little endian samples in native format
+ S16_LE signed 16bit little endian samples
+ S16_BE signed 16bit big endian samples
+ S24 signed 24bit little endian samples in native format
+ S24_LE signed 24bit little endian samples
+ S24_BE signed 24bit big endian samples
+ S32 signed 32bit little endian samples in native format
+ S32_LE signed 32bit little endian samples
+ S32_BE signed 32bit big endian samples
+ F32 float samples
+ MPEG audio mpeg encoded
+ MU_LAW u-law encoded 8bit samples
+ A_LAW a-law encoded 8bit samples
+ IMA_ADPCM adpcm encoded
the set of returned symbols depends on the underlying sound hardware.
"
- |fd audioFormatMask
- supportedFormats
- supports_MU_LAW supports_A_LAW supports_MPEG
- supports_IMA_ADPCM
- supports_S8 supports_U8
- supports_S16_LE supports_S16_BE
- supports_U16_LE supports_U16_BE
- supports_U32_LE supports_U32_BE
- supports_S24_LE supports_S24_BE
- supports_S32_LE supports_S32_BE
- supports_F32
- |
-
- fd := self fileDescriptorOrNil.
-
-%{
-#ifdef USE_DEV_AUDIO
- supports_MU_LAW = true;
-
- if (fd != nil) {
- int f = __intVal(fd);
- int __audioFormatMask = 0;
-
-# if defined(SNDCTL_DSP_GETFMTS)
- if (ioctl(f, SNDCTL_DSP_GETFMTS, &__audioFormatMask) >= 0) {
- audioFormatMask = __MKSMALLINT(__audioFormatMask);
-
-# ifdef AFMT_MU_LAW
- supports_MU_LAW = (__audioFormatMask & AFMT_MU_LAW) ? true : false;
-# endif
-# ifdef AFMT_A_LAW
- supports_A_LAW = (__audioFormatMask & AFMT_A_LAW) ? true : false;
-# endif
-# ifdef AFMT_IMA_ADPCM
- supports_IMA_ADPCM = (__audioFormatMask & AFMT_IMA_ADPCM) ? true : false;
-# endif
-# ifdef AFMT_U8
- supports_U8 = (__audioFormatMask & AFMT_U8) ? true : false;
-# endif
-# ifdef AFMT_S16_LE
- supports_S16_LE = (__audioFormatMask & AFMT_S16_LE) ? true : false;
-# endif
-# ifdef AFMT_S16_BE
- supports_S16_BE = (__audioFormatMask & AFMT_S16_BE) ? true : false;
-# endif
-# ifdef AFMT_S8
- supports_S8 = (__audioFormatMask & AFMT_S8) ? true : false;
-# endif
-# ifdef AFMT_U16_LE
- supports_U16_LE = (__audioFormatMask & AFMT_U16_LE) ? true : false;
-# endif
-# ifdef AFMT_U16_BE
- supports_U16_BE = (__audioFormatMask & AFMT_U16_BE) ? true : false;
-# endif
-# ifdef AFMT_MPEG
- supports_MPEG = (__audioFormatMask & AFMT_MPEG) ? true : false;
-# endif
- }
- }
-# endif // SNDCTL_DSP_GETFMTS
-
-# ifdef hpux
- supports_A_LAW = true;
- supports_S16_BE = true;
-# endif
-#endif /* USE_DEV_AUDIO */
-
-#ifdef USE_IRIS_AUDIO
- supports_U8 = true;
- supports_U16_BE = true;
-#endif
-
-#ifdef USE_PORTAUDIO
- supports_U8 = true;
- supports_S8 = true;
- supports_S16_LE = true;
- supports_S24_LE = true;
- supports_S32_LE = true;
- supports_F32 = true;
-#endif
-
-#ifdef __win32__
- supports_S16_LE = true;
-#endif
-
-%}.
- supportedFormats := IdentitySet new.
- (supports_MU_LAW ? false) ifTrue:[
- supportedFormats add:#'MU_LAW'
- ].
- (supports_A_LAW ? false) ifTrue:[
- supportedFormats add:#'A_LAW'
- ].
- (supports_IMA_ADPCM ? false) ifTrue:[
- supportedFormats add:#'IMA_ADPCM'
- ].
- (supports_MPEG ? false) ifTrue:[
- supportedFormats add:#'MPEG'
- ].
- (supports_S8 ? false) ifTrue:[
- supportedFormats add:#'S8'
- ].
- (supports_U8 ? false) ifTrue:[
- supportedFormats add:#'U8'
- ].
- (supports_S16_LE ? false) ifTrue:[
- supportedFormats add:#'S16_LE'.
- UninterpretedBytes isBigEndian ifFalse:[
- supportedFormats add:#'S16'.
- ]
- ].
- (supports_S16_BE ? false) ifTrue:[
- supportedFormats add:#'S16_BE'.
- UninterpretedBytes isBigEndian ifTrue:[
- supportedFormats add:#'S16'.
- ]
- ].
- (supports_S24_LE ? false) ifTrue:[
- supportedFormats add:#'S24_LE'.
- UninterpretedBytes isBigEndian ifFalse:[
- supportedFormats add:#'S24'.
- ]
- ].
- (supports_S24_BE ? false) ifTrue:[
- supportedFormats add:#'S24_BE'.
- UninterpretedBytes isBigEndian ifTrue:[
- supportedFormats add:#'S24'.
- ]
- ].
- (supports_S32_LE ? false) ifTrue:[
- supportedFormats add:#'S32_LE'.
- UninterpretedBytes isBigEndian ifFalse:[
- supportedFormats add:#'S32'.
- ]
- ].
- (supports_S32_BE ? false) ifTrue:[
- supportedFormats add:#'S32_BE'.
- UninterpretedBytes isBigEndian ifTrue:[
- supportedFormats add:#'S32'.
- ]
- ].
- (supports_U16_LE ? false) ifTrue:[
- supportedFormats add:#'U16_LE'.
- UninterpretedBytes isBigEndian ifFalse:[
- supportedFormats add:#'U16'.
- ]
- ].
- (supports_U16_BE ? false) ifTrue:[
- supportedFormats add:#'U16_BE'.
- UninterpretedBytes isBigEndian ifTrue:[
- supportedFormats add:#'U16'.
- ]
- ].
- (supports_F32 ? false) ifTrue:[
- supportedFormats add:#'F32'
- ].
- ^ supportedFormats.
+ ^ #( U8)
"
|s formats|
@@ -2013,155 +1403,7 @@
closeFile
"a stream has been collected - close the file"
-%{
-#ifdef USE_IRIS_AUDIO
- OBJ port;
-
- if ((port = __INST(alPort)) != nil) {
- __INST(alPort) = nil;
- ALcloseport(__ALportVal(port));
- }
- RETURN (self);
-#endif /* USE_IRIS_AUDIO */
-
-#ifdef USE_WIN32_DIRECTSOUND
- OBJ oDirectSound, oDSBuffer;
- LPDIRECTSOUND t_pDirectSound;
- LPDIRECTSOUNDBUFFER t_pDSBuffer;
-
- if ((oDSBuffer = __INST(pDSBuffer)) != nil) {
- __INST(pDSBuffer) = nil;
- t_pDSBuffer = __DSBufferVal(oDSBuffer);
- if (t_pDSBuffer) {
- IDirectSoundBuffer_Stop(t_pDSBuffer);
- IDirectSoundBuffer_Release(t_pDSBuffer);
- }
- }
- if ((oDirectSound = __INST(pDirectSound)) != nil) {
- __INST(pDirectSound) = nil;
- t_pDirectSound = __DirectSoundVal(oDirectSound);
- if (t_pDirectSound) {
- IDirectSound_Release(t_pDirectSound);
- }
- }
- RETURN (self);
-#endif /* USE_WIN32_DIRECTSOUND */
-
-#ifdef USE_WIN32_WAVESOUND
- struct buf *bp, *next;
- int r;
- HWAVEOUT t_waveHandle;
- OBJ oWaveHandle;
-
- if ((oWaveHandle = __INST(waveHandle)) != nil) {
- t_waveHandle = __WaveHandleVal(oWaveHandle);
-
-# ifdef NO_WAIT_IN_CLOSE
- /* Force cancellation of any pending buffers */
- (void)waveOutReset(t_waveHandle);
-# endif
-
- /* Wait until all pending buffers have been freed */
- while (free_buffers < total_buffers) {
- WaitForSingleObject(free_buffer_event, INFINITE);
- }
-
-# ifndef NO_WAIT_IN_CLOSE
- /* Force cancellation of any pending buffers */
- (void)waveOutReset(t_waveHandle);
-# endif
-
- /* Close the device */
- if ((r = waveOutClose(t_waveHandle)) != 0) {
- console_printf("waveOutClose\n");
- RETURN(self);
- }
-
- EnterCriticalSection(&free_list_lock);
-
- /* Free allocated buffers */
- for (bp = free_list; bp != NULL; bp = next) {
- next = bp->next;
- (void)free(bp);
- }
- free_list = NULL;
-
- LeaveCriticalSection(&free_list_lock);
-
- __INST(waveHandle) = nil;
- }
- RETURN (self);
-#endif /* USE_WIN32_WAVESOUND */
-
-#ifdef USE_DEV_AUDIO
- OBJ fp;
- int fd;
- FILE *f;
-
- if ((fp = __INST(handle)) != nil) {
- f = __FILEVal(fp);
- __INST(handle) = nil;
-# ifdef LINUX
- sigsetmask(~0);
-# endif
- if (__INST(buffered) == true) {
- fflush(f);
- fclose(f);
- } else {
- fd = fileno(f);
- close(fd);
- fclose(f);
- }
-# ifdef LINUX
- sigsetmask(0);
-# endif
- }
- RETURN (self);
-#endif /* USE_DEV_AUDIO */
-
-#ifdef USE_PORTAUDIO
- OBJ str;
- if ((str = __INST(handle1)) != nil) {
- struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
- struct paBuffer* buffer;
-
- if (DEBUGGING) {
- fprintf(stderr, "pa close\n");
- }
- __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;
- while (buffer != NULL) {
- struct paBuffer* nextBuffer = buffer->nextBuffer;
- free(buffer->sampleData);
- free(buffer);
- buffer = nextBuffer;
- }
- buffer = paStreamData->freeList;
- paStreamData->freeList = NULL;
- while (buffer != NULL) {
- struct paBuffer* nextBuffer = buffer->nextBuffer;
- free(buffer->sampleData);
- free(buffer);
- buffer = nextBuffer;
- }
-
- UNLOCK(paStreamData->lock);
- RELEASELOCK(paStreamData->lock);
- free(paStreamData);
- }
- RETURN (self);
-#endif // USE_PORTAUDIO
-
-%}.
- ^ super closeFile
+ self subclassResponsibility
! !
!SoundStream methodsFor:'redefined'!
@@ -2169,49 +1411,8 @@
flush
"wait until all sound has been played"
- |fd|
-
- fd := self fileDescriptorOrNil.
-%{
-#ifdef USE_IRIS_AUDIO
- OPJ port;
- ALport p;
-
- if ((port = __INST(alPort)) != nil) {
- p = __ALportVal(port);
- while (ALgetfilled(p) > 0) {
- sginap(1);
- }
- }
- RETURN(self);
-#endif /* USE_IRIS_AUDIO */
-
-#ifdef USE_DEV_AUDIO
- if (__isSmallInteger(fd)) {
- int f = __intVal(fd);
- /* ... */
- }
-#endif /* USE_DEV_AUDIO */
-
-#ifdef USE_PORTAUDIO
- OBJ str;
- if ((str = __INST(handle1)) != nil) {
- struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
-
- while (paStreamData->currentBuffer != NULL) {
- struct timespec a;
- a.tv_sec = 0;
- a.tv_nsec = 1 * 1000000; // 1 milliseconds asNanoseconds -> 1000000
- nanosleep( &a, NULL );
- }
- }
-#endif /* USE_PORTAUDIO */
-
-#ifdef USE_IRIS_AUDIO
- // don't know how to wait on non-iris systems
-#endif /* USE_IRIS_AUDIO */
-%}.
- ^ self
+ self subclassResponsibility
+
!
isOpen
@@ -2230,107 +1431,7 @@
bytes read or nil on error.
Use with ByteArrays only."
-%{
-#ifdef USE_IRIS_AUDIO
- {
- OBJ port;
- ALport p;
- int cnt, offs, nSamp;
- int objSize;
- char *cp;
-
- if ((port = __INST(alPort)) != nil) {
- if (__INST(mode) != _writeonly) {
- if (__bothSmallInteger(count, start)) {
- cnt = __intVal(count);
- offs = __intVal(start) - 1;
- p = __ALportVal(port);
- objSize = _Size(anObject) - OHDR_SIZE;
- if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
- cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
- if (__INST(bitsPerSample) == __MKSMALLINT(16))
- nSamp = cnt / 2;
- else
- nSamp = cnt;
- ALreadsamps(p, cp, nSamp);
- RETURN ( __MKSMALLINT(cnt) );
- }
- }
- }
- }
- }
-#endif /* USE_IRIS_AUDIO */
-
-#ifdef USE_DEV_AUDIO
- /*
- * redefine to work around a bug in the linux sound driver;
- * if a write is interrupted (EINTR), it is not defined, how many
- * bytes have been read from the device.
- *
- * As a workaround, disable signals here to prevent the write from
- * being interrupted.
- */
- int cnt, offs, objSize, n;
- char *cp;
- OBJ fp;
- FILE *f;
- int fd;
-
- if ((fp = __INST(handle)) != nil) {
- f = __FILEVal(fp);
- if (__INST(mode) != @symbol(writeonly)) {
- if (__bothSmallInteger(count, start)) {
- cnt = __intVal(count);
- offs = __intVal(start) - 1;
-
- objSize = _Size(anObject) - OHDR_SIZE;
- if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
- do {
- cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
-
- n = cnt;
-/* if (n > 4096) n = 4096; */
-# ifdef LINUX
- sigsetmask(~0);
-# endif
- if (__INST(buffered) == true) {
- n = fread(cp, 1, n, f);
- } else {
- fd = fileno(f);
- n = read(fd, cp, n);
- }
-# ifdef LINUX
- sigsetmask(0);
-# endif
- __BEGIN_INTERRUPTABLE__
- __END_INTERRUPTABLE__
-/* console_printf("SoundStream: read %d bytes\n", n); */
- if (n > 0) {
- offs += n;
- cnt -= n;
- } else {
- if (n < 0) {
- console_fprintf(stderr, "read error: %d\n", __threadErrno);
- RETURN (count);
- }
- }
- } while (cnt);
- }
- RETURN (count);
- }
- }
- }
-#endif /* USE_DEV_AUDIO */
-
-%}.
- OperatingSystem getOSType = 'irix' ifFalse:[
- OperatingSystem getOSType = 'win32' ifFalse:[
- ^ super nextPutBytes:count from:anObject startingAt:start
- ].
- ].
- handle isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- self primitiveFailed
+ self subclassResponsibility
!
nextPutBytes:count from:anObject startingAt:start
@@ -2339,784 +1440,11 @@
Redefined, since IRIS audio library cannot be used with stdio.
(at least I don't know). Use with ByteArrays only."
-%{
-#ifdef USE_IRIS_AUDIO
- {
- OBJ port;
- ALport p;
- int cnt, offs, nSamp;
- int objSize;
- char *cp;
-
- if ((port = __INST(alPort)) != nil) {
- if (__INST(mode) != @symbol(readonly)) {
- if (__bothSmallInteger(count, start)) {
- cnt = __intVal(count);
- offs = __intVal(start) - 1;
- p = __ALportVal(port);
-
- /*
- * compute number of samples
- */
- objSize = _Size(anObject) - OHDR_SIZE;
- if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
- cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
- if (__INST(bitsPerSample) == __MKSMALLINT(16))
- nSamp = cnt / 2;
- else
- nSamp = cnt;
- ALwritesamps(p, cp, cnt);
- RETURN ( count );
- }
- }
- }
- }
- }
-#endif /* USE_IRIS_AUDIO */
-
-#ifdef USE_WIN32_DIRECTSOUND
- {
- HRESULT hr;
- DWORD status;
- LPVOID lpbuf1 = NULL;
- LPVOID lpbuf2 = NULL;
- DWORD dwsize1 = 0;
- DWORD dwsize2 = 0;
- DWORD playPos, safePos, endWrite;
- DWORD millis;
- OBJ oDirectSound, oDSBuffer;
- LPDIRECTSOUND t_pDirectSound = (LPDIRECTSOUND)0;
- LPDIRECTSOUNDBUFFER t_pDSBuffer = (LPDIRECTSOUNDBUFFER)0;
- int t_cbBufOffset, t_cbBufSize;
- short *buf;
- int cnt, offs;
-
- if ((oDSBuffer = __INST(pDSBuffer)) != nil) {
- t_pDSBuffer = __DSBufferVal(oDSBuffer);
- }
- if ((oDirectSound = __INST(pDirectSound)) != nil) {
- t_pDirectSound = __DirectSoundVal(oDirectSound);
- }
-
- if (!t_pDSBuffer || !t_pDirectSound) {
- console_fprintf(stderr, "SoundStream not open!\n");
- RETURN (0);
- }
- t_cbBufOffset = __intVal(__INST(bufferOffset));
- t_cbBufSize = __intVal(__INST(bufferSize));
-
- cnt = __intVal(count);
- offs = __intVal(start) - 1;
- buf = (short *)__InstPtr(anObject) + OHDR_SIZE + offs;
-
- // Should be playing, right?
- hr = IDirectSoundBuffer_GetStatus(t_pDSBuffer, &status );
- if (!(status && DSBSTATUS_PLAYING)) {
- console_fprintf(stderr, "Buffer not playing!\n");
- RETURN (0);
- }
-
- // Sleep until we have enough room in buffer.
- hr = IDirectSoundBuffer_GetCurrentPosition(t_pDSBuffer, &playPos, &safePos );
- if( hr != DS_OK ) {
- console_fprintf(stderr, "Cannot get position!\n");
- RETURN (0);
- }
- if( playPos < t_cbBufOffset ) playPos += t_cbBufSize;
-
- endWrite = t_cbBufOffset + (cnt * sizeof(short));
- while ( playPos < endWrite ) {
- // Calculate number of milliseconds until we will have room, as
- // time = distance * (milliseconds/second) / ((bytes/sample) * (samples/second)),
- // rounded up.
- millis = (DWORD) (1.0 + ((endWrite - playPos) * 1000.0) / ( sizeof(short) * __intVal(__INST(sampleRate))));
-
- // Sleep for that long
- Sleep( millis );
-
- // Wake up, find out where we are now
- hr = IDirectSoundBuffer_GetCurrentPosition(t_pDSBuffer, &playPos, &safePos );
- if( hr != DS_OK ) {
- console_fprintf(stderr, "Cannot get position!\n");
- RETURN (0);
- }
- if( playPos < t_cbBufOffset ) playPos += t_cbBufSize; // unwrap offset
- }
-
- // Lock free space in the DS
- hr = IDirectSoundBuffer_Lock(t_pDSBuffer, t_cbBufOffset, cnt * sizeof(short), &lpbuf1, &dwsize1, &lpbuf2, &dwsize2, 0);
- if (hr == DS_OK) {
- // Copy the buffer into the DS
- CopyMemory(lpbuf1, buf, dwsize1);
- if(NULL != lpbuf2) CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
-
- // Update our buffer offset and unlock sound buffer
- t_cbBufOffset = (t_cbBufOffset + dwsize1 + dwsize2) % t_cbBufSize;
- IDirectSoundBuffer_Unlock(t_pDSBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
- }
- __INST(buffferOffset) = __MKSMALLINT(t_cbBufOffset);
-
- RETURN (count);
- }
-#endif /* USE_WIN32_DIRECTSOUND */
-
-#ifdef USE_WIN32_WAVESOUND
- {
- struct buf *bp;
- int len, i, r;
- int dataLen, offs;
- short *buf;
-
- HWAVEOUT t_waveHandle;
- OBJ oWaveHandle;
-
- if ((oWaveHandle = __INST(waveHandle)) == nil) {
- RETURN(0);
- }
- t_waveHandle = __WaveHandleVal(oWaveHandle);
-
- dataLen = __intVal(count);
- offs = __intVal(start) - 1;
- buf = (short *)__InstPtr(anObject) + OHDR_SIZE + offs;
-
- while (dataLen > 0) {
- if (free_list == NULL && total_buffers < MAXBUF) {
- /* Expand available buffer space */
- bp = (struct buf *)malloc(sizeof(struct buf));
- total_buffers++;
- } else {
- if (free_list == NULL) {
- /* We must wait for a free buffer */
- while (free_list == NULL) {
- WaitForSingleObject(free_buffer_event, INFINITE);
- }
- }
- EnterCriticalSection(&free_list_lock);
- bp = free_list;
- free_list = free_list->next;
- --free_buffers;
- LeaveCriticalSection(&free_list_lock);
- r = waveOutUnprepareHeader(t_waveHandle, &bp->hdr, sizeof(WAVEHDR));
- if (r != 0) {
- console_printf("waveOutUnprepareHeader\n");
- RETURN(self);
- }
- }
- len = min(dataLen, DATALEN);
- bp->hdr.lpData = (char *)bp->data;
- bp->hdr.dwBufferLength = len;
- bp->hdr.dwBytesRecorded = len;
- bp->hdr.dwUser = (INT)(bp);
- bp->hdr.dwFlags = 0;
- bp->hdr.dwLoops = 0;
- r = waveOutPrepareHeader(t_waveHandle, &bp->hdr, sizeof(WAVEHDR));
- if (r != 0) {
- console_printf("waveOutPrepareHeader\n");
- RETURN(self);
- }
- for (i = 0; i < len; i++) {
- bp->data[i] = buf[i];
- }
- r = waveOutWrite(t_waveHandle, &bp->hdr, sizeof(WAVEHDR));
- if (r != 0) {
- console_printf("waveOutWrite\n");
- RETURN(self);
- }
- buf += len;
- dataLen -= len;
- }
- RETURN (count);
- }
-#endif /* USE_WIN32_WAVESOUND */
-
-#ifdef USE_DEV_AUDIO
- /*
- * redefine to work around a bug in the linux sound driver;
- * if a write is interrupted (EINTR), it is not defined, how many
- * bytes have been written to the device.
- * I.e. a retry of the write may lead to ever-playing without ever
- * finishing.
- *
- * As a workaround, disable signals here to prevent the write from
- * being interrupted.
- */
- int cnt, offs, objSize, n;
- char *cp;
- OBJ fp;
- FILE *f;
- int fd;
-
- if ((fp = __INST(handle)) != nil) {
- f = __FILEVal(fp);
- if (__INST(mode) != @symbol(readonly)) {
- if (__bothSmallInteger(count, start)) {
- cnt = __intVal(count);
- offs = __intVal(start) - 1;
-
- objSize = _Size(anObject) - OHDR_SIZE;
- if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
- do {
- cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
-
- n = cnt;
-# ifdef LINUX
- sigsetmask(~0);
-# endif
- if (__INST(buffered) == true) {
- n = fwrite(cp, 1, n, f);
- } else {
- fd = fileno(f);
- n = write(fd, cp, n);
- }
-# ifdef LINUX
- sigsetmask(0);
-# endif
- __BEGIN_INTERRUPTABLE__
- __END_INTERRUPTABLE__
- if (n > 0) {
- offs += n;
- cnt -= n;
- } else {
- if (n < 0) {
- console_fprintf(stderr, "write error: %d\n", __threadErrno);
- RETURN (count);
- }
- }
- } while (cnt);
- }
- RETURN (count);
- }
- }
- }
-#endif /* USE_DEV_AUDIO */
-
-#ifdef USE_PORTAUDIO
- OBJ str;
-
- if ((str = __INST(handle1)) != nil) {
- if (__INST(mode) != @symbol(readonly)) {
- if (__bothSmallInteger(count, start)) {
- // allocate a buffer
- struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
- int cnt = __intVal(count);
- int offs = __intVal(start) - 1;
- int objSize;
-
- objSize = _Size(anObject) - OHDR_SIZE;
- if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
- struct paBuffer* newBuffer;
- struct paBuffer* toFree;
- int mustStart = 0;
- unsigned char* newSampleData;
-
- // try freeList
- LOCK(paStreamData->lock);
- if ((paStreamData->freeList != NULL)
- && (paStreamData->freeList->bufferSize == cnt)) {
- // reuse
- newBuffer = paStreamData->freeList;
- paStreamData->freeList = newBuffer->nextBuffer;
- newSampleData = newBuffer->sampleData;
- toFree = NULL;
- if (DEBUGGING) {
- fprintf(stderr, "from free: %p (->%p)\n", newBuffer, newBuffer->sampleData);
- }
- } else {
- // free them all
- toFree = paStreamData->freeList;
- paStreamData->freeList = NULL;
- newBuffer = malloc(sizeof(struct paBuffer));
- newSampleData = malloc(cnt);
- newBuffer->sampleData = newSampleData;
- newBuffer->bufferSize = cnt;
- if (DEBUGGING) {
- fprintf(stderr, "alloc: %p (->%p)\n", newBuffer, newBuffer->sampleData);
- }
- }
-
- memcpy(newSampleData, (__ByteArrayInstPtr(anObject)->ba_element)+offs, cnt);
- newBuffer->nextBuffer = NULL;
-
- if (paStreamData->lastBuffer == NULL) {
- // start stream's buffer list
- paStreamData->currentBuffer = newBuffer;
- mustStart = 1;
- } else {
- // append to stream's buffer list
- paStreamData->lastBuffer->nextBuffer = newBuffer;
- }
- paStreamData->lastBuffer = newBuffer;
- paStreamData->hasFinished = 0;
- UNLOCK(paStreamData->lock);
-
- if (mustStart) {
- PaError paErr = Pa_StartStream( paStreamData->stream );
- if ( paErr != paNoError ) {
- if (DEBUGGING) {
- fprintf(stderr, "start error\n");
- }
- RETURN (0);
- };
- }
- RETURN (count);
- }
- }
- }
- }
-#endif /* USE_PORTAUDIO */
-
-%}.
- ^ super nextPutBytes:count from:anObject startingAt:start
+ self subclassResponsibility
!
openWithMode:aMode attributes:attributeSpec
- |ok error errorStringOrNil|
-
-%{
-#ifdef USE_IRIS_AUDIO
- {
- ALconfig config;
- ALport p;
- long params[] = {
- AL_INPUT_SOURCE, AL_INPUT_MIC,
- AL_INPUT_RATE, 8000,
- AL_OUTPUT_RATE, 8000,
- };
-
- config = ALnewconfig();
- if (__INST(numberOfChannels) == __MKSMALLINT(2))
- ALsetchannels(config, AL_STEREO);
- else
- ALsetchannels(config, AL_MONO);
- if (__INST(bitsPerSample) == __MKSMALLINT(16))
- ALsetwidth(config, AL_SAMPLE_16);
- else
- ALsetwidth(config, AL_SAMPLE_8);
-
- if (__isSmallInteger(__INST(sampleRate)))
- params[3] = params[5] = __intVal(__INST(sampleRate));
-
- ALsetparams(AL_DEFAULT_DEVICE, params, 6);
- p = ALopenport("smallchat", (char *)_stringVal(aMode), config);
- if (p) {
- OBJ t;
-
- t = __MKEXTERNALADDRESS(p); __INST(alPort) = t; __STORE(self, t);
- } else {
- __INST(alPort) = nil;
- goto out;
- }
- __INST(binary) = true;
-
- ALfreeconfig(config);
- /*
- * get the parameters actually installed
- */
- config = ALgetconfig(p);
- switch (ALgetchannels(config)) {
- default:
- /* cannot happen */
- case AL_MONO:
- __INST(numberOfChannels) = __MKSMALLINT(1);
- break;
- case AL_STEREO:
- __INST(numberOfChannels) = __MKSMALLINT(2);
- break;
- }
- switch (ALgetwidth(config)) {
- default:
- /* cannot happen */
- case AL_SAMPLE_8:
- __INST(bitsPerSample) = __MKSMALLINT(8);
- break;
- case AL_SAMPLE_16:
- __INST(bitsPerSample) = __MKSMALLINT(16);
- break;
- case AL_SAMPLE_24:
- __INST(bitsPerSample) = __MKSMALLINT(24);
- break;
- }
- ALgetparams(AL_DEFAULT_DEVICE, params, 6);
- __INST(sampleRate) = __MKSMALLINT(params[3]);
-
- ALfreeconfig(config);
- ok = true;
- goto out;
- }
-#endif /* USE_IRIS_AUDIO */
-
-#ifdef USE_WIN32_DIRECTSOUND
- {
- HRESULT result;
- LPDIRECTSOUND t_pDirectSound;
- LPDIRECTSOUNDBUFFER t_pDSBuffer, t_pDSPrimeBuffer;
- WAVEFORMATEX wfFormat;
- DSBUFFERDESC dsbdDesc, primarydsbDesc;
- BYTE *pDSBuffData;
- int t_cbBufSize;
- int dwDataLen;
-
- /* Create the DS object */
- if ((result = DirectSoundCreate(NULL, &t_pDirectSound, NULL)) != DS_OK) {
- console_fprintf(stderr,"SoundStream: Cannot open default sound device!!\n");
- goto out;
- }
-
- /* Define the wave format structure */
- wfFormat.wFormatTag = WAVE_FORMAT_PCM;
- wfFormat.nChannels = __intVal(__INST(numberOfChannels));
- wfFormat.nSamplesPerSec = __intVal(__INST(sampleRate));
- wfFormat.wBitsPerSample = __intVal(__INST(bitsPerSample));
- wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8;
- wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
- wfFormat.cbSize = 0;
-# if 0
- /* Setup the primary DS buffer description */
- ZeroMemory(&primarydsbDesc, sizeof(DSBUFFERDESC));
- primarydsbDesc.dwSize = sizeof(DSBUFFERDESC);
- primarydsbDesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
- primarydsbDesc.dwBufferBytes = 0;
- primarydsbDesc.lpwfxFormat = NULL;
-
- /* Create the primary DS buffer */
- if ((result = IDirectSound_CreateSoundBuffer(t_pDirectSound, &primarydsbDesc,
- &t_pDSPrimeBuffer, NULL)) != DS_OK) {
- console_fprintf(stderr,"SoundStream: Cannot get the primary DS buffer address!\n");
- IDirectSound_Release(t_pDirectSound);
- goto out;
- }
-
- /* Set the primary DS buffer sound format. We have to do this because
- the default primary buffer is 8-bit, 22kHz! */
- if ((result = IDirectSoundBuffer_SetFormat(t_pDSPrimeBuffer, &wfFormat)) != DS_OK) {
- console_fprintf(stderr,"SoundStream: Cannot set the primary DS buffer to proper sound format (%x) (%d)!\n", result, result);
- IDirectSoundBuffer_Stop(t_pDSPrimeBuffer);
- IDirectSoundBuffer_Release(t_pDSPrimeBuffer);
- IDirectSound_Release(t_pDirectSound);
- goto out;
- }
-# endif /* 0 */
-
- /* Setup the secondary DS buffer description */
- t_cbBufSize = RT_BUFFER_SIZE * sizeof(short) * NBUFS;
- __INST(bufferSize) = __MKSMALLINT(t_cbBufSize);
-
- ZeroMemory(&dsbdDesc, sizeof(DSBUFFERDESC));
- dsbdDesc.dwSize = sizeof(DSBUFFERDESC);
- dsbdDesc.dwFlags = DSBCAPS_GLOBALFOCUS;
- dsbdDesc.dwBufferBytes = t_cbBufSize;
- dsbdDesc.lpwfxFormat = &wfFormat;
-
- /* Create the secondary DS buffer */
- if ((result = IDirectSound_CreateSoundBuffer(t_pDirectSound, &dsbdDesc, &t_pDSBuffer, NULL)) != DS_OK) {
- console_fprintf(stderr,"SoundStream: couldn't create sound buffer!\n");
- IDirectSoundBuffer_Stop(t_pDSPrimeBuffer);
- IDirectSoundBuffer_Release(t_pDSPrimeBuffer);
- IDirectSound_Release(t_pDirectSound);
- goto out;
- }
-
- /* Lock the DS buffer */
- if ((result = IDirectSoundBuffer_Lock(t_pDSBuffer, 0, t_cbBufSize, (LPLPVOID)&pDSBuffData,
- &dwDataLen, NULL, NULL, 0)) != DS_OK) {
- console_fprintf(stderr,"SoundStream: couldn't lock sound buffer!\n");
- goto errorAndOut;
- }
-
- /* Zero the DS buffer */
- ZeroMemory(pDSBuffData, dwDataLen);
-
- /* Unlock the DS buffer */
- if ((result = IDirectSoundBuffer_Unlock(t_pDSBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) {
- console_fprintf(stderr,"SoundStream: couldn't unlock sound buffer!\n");
- goto errorAndOut;
- }
-
- __INST(bufferOffset) = __MKSMALLINT(0); // reset last write position to start of buffer
-
- /* Start the buffer playback */
- if ((result = IDirectSoundBuffer_Play(t_pDSBuffer, 0, 0, DSBPLAY_LOOPING) != DS_OK)) {
- console_fprintf(stderr,"SoundStream: couldn't play sound buffer!\n");
-errorAndOut:
- IDirectSoundBuffer_Stop(t_pDSBuffer);
- IDirectSoundBuffer_Stop(t_pDSPrimeBuffer);
- IDirectSoundBuffer_Release(t_pDSPrimeBuffer);
- IDirectSound_Release(t_pDirectSound);
- ok = false;
- goto out;
- }
-
- {
- OBJ t;
-
- t = __MKEXTERNALADDRESS(t_pDSBuffer); __INST(pDSBuffer) = t; __STORE(self, t);
- t = __MKEXTERNALADDRESS(t_pDirectSound); __INST(pDirectSound) = t; __STORE(self, t);
- }
- ok = true;
- goto out;
- }
-#endif /* USE_WIN32_DIRECTSOUND */
-
-#ifdef USE_WIN32_WAVESOUND
- {
- PCMWAVEFORMAT waveFormat;
- int r;
- HWAVEOUT t_waveHandle;
- OBJ oWaveHandle;
-
- if ((oWaveHandle = __INST(waveHandle)) != nil) {
- ok = false;
- goto out;
- }
-
- waveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
- waveFormat.wf.nChannels = __intVal(__INST(numberOfChannels));
- waveFormat.wf.nSamplesPerSec = __intVal(__INST(sampleRate));
- waveFormat.wBitsPerSample = __intVal(__INST(bitsPerSample));
- waveFormat.wf.nBlockAlign = waveFormat.wf.nChannels * waveFormat.wBitsPerSample / 8;
- waveFormat.wf.nAvgBytesPerSec = waveFormat.wf.nSamplesPerSec * waveFormat.wf.nBlockAlign;
-
- r = waveOutOpen(&t_waveHandle,
- WAVE_MAPPER,
- (WAVEFORMAT *)&waveFormat,
- (DWORD_PTR)waveCallBack,
- (DWORD_PTR)0,
- CALLBACK_FUNCTION);
- if (r != 0) {
- console_printf("waveOutOpen\n");
- ok = false;
- goto out;
- }
-
- (void)waveOutReset(t_waveHandle);
-
- free_list = NULL;
- InitializeCriticalSection(&free_list_lock);
- free_buffers = 0;
- free_buffer_event = CreateEvent(NULL, FALSE, FALSE, NULL);
- total_buffers = 0;
-
- {
- OBJ t;
-
- t = __MKEXTERNALADDRESS(t_waveHandle); __INST(waveHandle) = t; __STORE(self, t);
- }
-
-# if 0
- /*
- * HACK: If we immediately start writing valid audio data to the device
- * then the sound is choppy in the beginning. Writing a null packet to
- * to the device first seems to fix this problem although I have no idea
- * why - DAC
- */
- {
- char null[DATALEN];
- int i;
- for (i = 0; i < DATALEN; i++) {
- null[i] = 127;
- }
- audioWrite(null, DATALEN);
- }
-# endif /* 0 */
- ok = true;
- goto out;
- }
-#endif /* !USE_WIN32_DIRECTSOUND */
-
-#ifdef USE_DEV_AUDIO
- {
- int __fd;
- int __mode;
- FILE *f;
-
- if (strcmp(__stringVal(aMode), "w") == 0) {
- __mode = O_WRONLY;
- } else if (strcmp(__stringVal(aMode), "r") == 0) {
- __mode = O_RDONLY;
- } else {
- __mode = O_RDWR;
- }
- do {
- __BEGIN_INTERRUPTABLE__
- __fd = open((char *) __stringVal(__INST(pathName)), __mode /* |O_NDELAY */);
- __END_INTERRUPTABLE__
- } while ((__fd < 0) && (errno == EINTR));
-
- if (__fd >= 0) {
- /*
- * make it a FILE *
- */
- f = fdopen(__fd, __stringVal(aMode));
- if (! f) {
- error = __mkSmallInteger(errno);
- __BEGIN_INTERRUPTABLE__
- close(__fd);
- __END_INTERRUPTABLE__
- ok = false;
- goto out;
- }
- setbuf(f, NULL);
- __INST(buffered) = false;
- __INST(handle) = __MKEXTERNALADDRESS(f);
- __STORESELF(handle);
-
-#if defined(PCM_ENABLE_OUTPUT) && defined(PCM_ENABLE_INPUT)
-# if defined(SNDCTL_DSP_SETTRIGGER)
- if (__mode == O_RDWR) {
- int enable_bits = ~(PCM_ENABLE_OUTPUT|PCM_ENABLE_INPUT);
-
- if (ioctl(__fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1)
- {
- console_fprintf(stderr, "can't request synchronous start of fullduplex operation");
- }
- }
-# endif
-#endif
- ok = true;
- goto out;
- } else {
- error = __mkSmallInteger(errno);
- }
- }
-#endif /* USE_DEV_AUDIO */
-
-#ifdef USE_PORTAUDIO
- {
- static PaStreamParameters outputParameters;
- PaStream *stream;
- PaError paErr;
- struct paStreamData* paStreamData;
- int nChannels, sampleRate, bytesPerSample;
-# define FRAMES_PER_BUFFER 128
-
- /* default output device */
- outputParameters.device = Pa_GetDefaultOutputDevice();
- if (outputParameters.device == paNoDevice) {
- fprintf(stderr, "SoundStream [warning]: No default output device.\n");
- ok = false;
- errorStringOrNil = __MKSTRING("No default output device");
- goto out;
- }
-
- if (__isSmallInteger(__INST(numberOfChannels))) {
- nChannels = __intVal(__INST(numberOfChannels));
- } else {
- nChannels = 1;
- }
- outputParameters.channelCount = nChannels;
- outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
- outputParameters.hostApiSpecificStreamInfo = NULL;
-
- // The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
- // and aUInt8 are usually implemented by all implementations.
- // The floating point representation (paFloat32) uses +1.0 and -1.0 as the
- // maximum and minimum respectively.
- // paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
-
- if (__INST(audioFormat) == @symbol(S16)) {
- outputParameters.sampleFormat = paInt16;
- bytesPerSample = 2;
- } else if (__INST(audioFormat) == @symbol(S32)) {
- outputParameters.sampleFormat = paInt32;
- bytesPerSample = 4;
- } else if (__INST(audioFormat) == @symbol(S24)) {
- outputParameters.sampleFormat = paInt24;
- bytesPerSample = 3;
- } else if (__INST(audioFormat) == @symbol(S8)) {
- outputParameters.sampleFormat = paInt8;
- bytesPerSample = 1;
- } else if (__INST(audioFormat) == @symbol(F32)) {
- outputParameters.sampleFormat = paFloat32;
- bytesPerSample = 4;
- } else if (__INST(audioFormat) == @symbol(U8)) {
- outputParameters.sampleFormat = paUInt8;
- bytesPerSample = 1;
- } else {
- fprintf(stderr, "SoundStream [warning]: unknown format - using U8\n");
- outputParameters.sampleFormat = paUInt8;
- bytesPerSample = 1;
- }
-
- if (__isSmallInteger(__INST(sampleRate))) {
- sampleRate = __intVal(__INST(sampleRate));
- } else {
- fprintf(stderr, "SoundStream [warning]: using default sampleRate 8000\n");
- sampleRate = 8000;
- }
-
- paStreamData = (struct paStreamData*)malloc(sizeof(struct paStreamData));
- if (paStreamData == NULL) {
- fprintf(stderr, "SoundStream [warning]: failed to allocate paStream\n");
- ok = false;
- errorStringOrNil = __MKSTRING("failed to allocate paStream");
- goto out;
- }
-
- paErr = Pa_OpenStream(
- &stream,
- NULL, /* no input */
- &outputParameters,
- sampleRate,
- FRAMES_PER_BUFFER,
- paClipOff, /* we won't output out of range samples so don't bother clipping them */
- paCallback,
- paStreamData );
-
- if (paErr != paNoError) {
- fprintf(stderr, "SoundStream [warning]: openStream: %s\n", Pa_GetErrorText( paErr ));
- free(paStreamData);
- ok = false;
- 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;
-
- {
- OBJ t;
- t = __MKEXTERNALADDRESS(paStreamData); __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);
- ok = false;
- errorStringOrNil = __MKSTRING(Pa_GetErrorText( paErr ));
- goto out;
- };
-
- ok = true;
- goto out;
- }
-#endif /* USE_PORTAUDIO */
-
-out:;
-%}.
- ok == false ifTrue:[
- lastErrorString := errorStringOrNil.
- lastErrorNumber := error ? -1.
- self openError:error.
- "normally not reached"
- ^ nil.
- ].
-
- ok isNil ifTrue:[
- "its a regular file open (i.e. /dev/audio) "
- ^ super openWithMode:aMode attributes:attributeSpec.
- ].
-
- self registerForFinalization.
- ^ self.
+ self subclassResponsibility
! !
!SoundStream methodsFor:'sine wave generation'!
@@ -3377,14 +1705,52 @@
!
tuneTone:freq
+ self tuneTone:freq seconds:3
+
+ "
+ SoundStream writing tuneTone:880; close
+ SoundStream writing setSampleRate:4000; tuneTone:440; close
+ SoundStream writing setSampleRate:4000; tuneTone:880; close
+ SoundStream writing setSampleRate:8000; tuneTone:440; close
+ SoundStream writing setSampleRate:8000; tuneTone:880; close
+ SoundStream writing setSampleRate:10000; tuneTone:440; close
+ SoundStream writing setSampleRate:10000; tuneTone:880; close
+
+ SoundStream writing setSampleRate:20000; tuneTone:440; close
+ SoundStream writing setSampleRate:20000; tuneTone:880; close
+
+ SoundStream writing setSampleRate:40000; tuneTone:440; close
+ SoundStream writing setSampleRate:40000; tuneTone:880; close
+ SoundStream writing setSampleRate:40000; tuneTone:1760; close
+ SoundStream writing setSampleRate:40000; tuneTone:3520; close
+ SoundStream writing setSampleRate:40000; tuneTone:7020; close
+ SoundStream writing setSampleRate:40000; tuneTone:14040; close
+
+ SoundStream writing setSampleRate:44100; tuneTone:440; close
+ SoundStream writing setSampleRate:44100; tuneTone:880; close
+ SoundStream writing setSampleRate:44100; tuneTone:1760; close
+ SoundStream writing setSampleRate:44100; tuneTone:3520; close
+ SoundStream writing setSampleRate:44100; tuneTone:7020; close
+ SoundStream writing setSampleRate:44100; tuneTone:14040; close
+
+ SoundStream writing setSampleRate:20000; dumpSettings; close
+ "
+
+ "Modified: / 31.1.1999 / 12:07:14 / cg"
+!
+
+tuneTone:freq seconds:nSeconds
((audioFormat startsWith:#S16)
or:[audioFormat startsWith:#U16]) ifTrue:[
- ^ self tuneTone16:freq
+ ^ self tuneTone16:freq seconds:nSeconds
+ ].
+ audioFormat == #F32 ifTrue:[
+ ^ self tuneToneF32:freq seconds:nSeconds
].
audioFormat == #MU_LAW ifTrue:[
- ^ self tuneToneMU:freq
+ ^ self tuneToneMU:freq seconds:nSeconds
].
- self tuneTone8:freq
+ self tuneTone8:freq seconds:nSeconds
"
SoundStream writing tuneTone:880; close
@@ -3475,10 +1841,10 @@
"Created: / 31.1.1999 / 12:05:17 / cg"
!
-tuneToneMU:freq
- "output some tone for 3 seconds in MU_LAW audioFormat - a test method"
-
- |buffer numSamples val scale oldFormat|
+tuneToneMU:freq seconds:nSeconds
+ "output some tone for nSeconds in MU_LAW audioFormat - a test method"
+
+ |buffer numSamples val scale restSamples|
"allocate memory for 1sec playing time"
numSamples := self sampleRate.
@@ -3488,17 +1854,18 @@
scale := freq * 2 * (Float pi).
1 to:numSamples do:[:i |
- val := (scale * i / numSamples) sin.
- val := (val * 16r7FFF) rounded.
- buffer at:i put:(self class linear16ToUlaw:val)
+ val := (scale * i / numSamples) sin.
+ val := (val * 16r7FFF) rounded.
+ buffer at:i put:(self class linear16ToUlaw:val)
].
- oldFormat := audioFormat.
- self setAudioFormat:#MU_LAW.
- 1 to:3 do:[:s |
- self nextPutBytes:numSamples from:buffer startingAt:1
+ 1 to:nSeconds truncated do:[:s |
+ self nextPutBytes:numSamples from:buffer startingAt:1
].
- self setAudioFormat:oldFormat.
+ restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated.
+ restSamples > 0 ifTrue:[
+ self nextPutBytes:restSamples from:buffer startingAt:1
+ ].
"of course, the frequency should be below half the
sampleRate - hear below ...
@@ -3512,6 +1879,2508 @@
"Modified: / 15.12.1997 / 13:46:19 / cg"
! !
+!SoundStream::DevAudio class methodsFor:'default values'!
+
+defaultSampleRate
+ "minimum, supported by all audio systems"
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ RETURN (__MKSMALLINT(DEV_AUDIO_DEFAULT_FREQ));
+#endif
+%}.
+
+ ^ 8000
+! !
+
+!SoundStream::DevAudio class methodsFor:'documentation'!
+
+documentation
+"
+ documentation to be added.
+
+ class:
+ <a short class summary here, describing what instances represent>
+
+ responsibilities:
+ <describing what my main role is>
+
+ collaborators:
+ <describing with whom and how I talk to>
+
+ API:
+ <public api and main messages>
+
+ example:
+ <a one-line examples on how to use - can also be in a separate example method>
+
+ implementation:
+ <implementation points>
+
+ [author:]
+ exept MBP
+
+ [instance variables:]
+
+ [class variables:]
+
+ [see also:]
+
+"
+! !
+
+!SoundStream::DevAudio class methodsFor:'queries'!
+
+isSupported
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ RETURN(true);
+#endif
+%}.
+ ^ false.
+! !
+
+!SoundStream::DevAudio methodsFor:'misc'!
+
+dumpSettings
+ "debugging interface - dump the current settings"
+
+ |fd blockSize speed channels stereo format|
+
+ fd := self fileDescriptorOrNil.
+
+ audioFormat == #S16 ifTrue:[
+ UninterpretedBytes isBigEndian ifTrue:[
+ audioFormat := #S16_BE
+ ] ifFalse:[
+ audioFormat := #S16_LE
+ ]
+ ].
+ audioFormat == #U16 ifTrue:[
+ UninterpretedBytes isBigEndian ifTrue:[
+ audioFormat := #U16_BE
+ ] ifFalse:[
+ audioFormat := #U16_LE
+ ]
+ ].
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ if (fd != nil) {
+ int f = __intVal(fd);
+ int __blockSize = -1;
+ int __speed = -1;
+ int __channels = __intVal(__INST(numberOfChannels));
+ int __stereo = __channels > 1;
+ int __format = -1;
+
+ channels = nil;
+ blockSize = nil;
+ stereo = nil;
+ speed = nil;
+
+# if defined(SNDCTL_DSP_GETBLKSIZE)
+ if (ioctl(f, SNDCTL_DSP_GETBLKSIZE, &__blockSize) >= 0) {
+ blockSize = __MKSMALLINT(__blockSize);
+ }
+# endif
+
+# if defined(SNDCTL_DSP_CHANNELS)
+ if (ioctl(f, SNDCTL_DSP_CHANNELS, &__channels) >= 0) {
+ channels = __MKSMALLINT(__channels);
+ stereo = __MKSMALLINT(__channels > 1);
+ }
+# else
+# if defined(SNDCTL_DSP_STEREO)
+ if (ioctl(f, SNDCTL_DSP_STEREO, &__stereo) >= 0) {
+ stereo = __MKSMALLINT(__stereo);
+ }
+# endif
+# endif
+
+# if defined(SNDCTL_DSP_SPEED)
+ if (ioctl(f, SNDCTL_DSP_SPEED, &__speed) >= 0) {
+ speed = __MKSMALLINT(__speed);
+ }
+# endif
+
+# if defined(SNDCTL_DSP_GETFMT)
+ if (ioctl(f, SNDCTL_DSP_GETFMT, &__format) >= 0) {
+ format = __MKSMALLINT(__format);
+ }
+# else
+# if defined(SNDCTL_DSP_SETFMT) && defined(AFMT_QUERY)
+ __format = AFMT_QUERY;
+ if (ioctl(f, SNDCTL_DSP_SETFMT, &__format) >= 0) {
+ switch (__format) {
+# ifdef AFMT_MU_LAW
+ case AFMT_MU_LAW:
+ format = @symbol(MU_LAW);
+ break;
+# endif
+# ifdef AFMT_A_LAW
+ case AFMT_A_LAW:
+ format = @symbol(A_LAW);
+ break;
+# endif
+# ifdef AFMT_U8
+ case AFMT_U8:
+ format = @symbol(U8);
+ break;
+# endif
+# ifdef AFMT_S8
+ case AFMT_S8:
+ format = @symbol(S8);
+ break;
+# endif
+# ifdef AFMT_S16_LE
+ case AFMT_S16_LE:
+ format = @symbol(S16_LE);
+ break;
+# endif
+# ifdef AFMT_S16_BE
+ case AFMT_S16_BE:
+ format = @symbol(S16_BE);
+ break;
+# endif
+# ifdef AFMT_U16_LE
+ case AFMT_U16_LE:
+ format = @symbol(U16_LE);
+ break;
+# endif
+# ifdef AFMT_U16_BE
+ case AFMT_U16_BE:
+ format = @symbol(U16_BE);
+ break;
+# endif
+# ifdef AFMT_MPEG
+ case AFMT_MPEG:
+ format = @symbol(MPEG);
+ break;
+# endif
+ default:
+ format = nil;
+ }
+ }
+ }
+# endif
+# endif
+#endif /* SUPPORT_DEV_AUDIO */
+
+%}.
+ format notNil ifTrue:[
+ Transcript show:'format: '; showCR:format
+ ].
+ blockSize notNil ifTrue:[
+ Transcript show:'blockSize: '; showCR:blockSize
+ ].
+ speed notNil ifTrue:[
+ Transcript show:'speed: '; showCR:speed
+ ].
+ channels notNil ifTrue:[
+ Transcript show:'channels: '; showCR:channels
+ ].
+ stereo notNil ifTrue:[
+ Transcript show:'stereo: '; showCR:stereo
+ ].
+
+ Transcript show:'supported audioFormats: '; showCR:(self supportedAudioFormats).
+
+ "
+ self writing dumpSettings; close
+ "
+!
+
+flush
+ "wait until all sound has been played"
+
+ self fileDescriptorOrNil isNil ifTrue:[^ self].
+ super flush.
+! !
+
+!SoundStream::DevAudio methodsFor:'open & close'!
+
+closeFile
+ "a stream has been collected - close the file"
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ OBJ fp;
+ int fd;
+ FILE *f;
+
+ if ((fp = __INST(handle)) != nil) {
+ f = __FILEVal(fp);
+ __INST(handle) = nil;
+# ifdef LINUX
+ sigsetmask(~0);
+# endif
+ if (__INST(buffered) == true) {
+ fflush(f);
+ fclose(f);
+ } else {
+ fd = fileno(f);
+ close(fd);
+ fclose(f);
+ }
+# ifdef LINUX
+ sigsetmask(0);
+# endif
+ }
+ RETURN (self);
+#endif /* SUPPORT_DEV_AUDIO */
+%}.
+!
+
+openWithMode:aMode attributes:attributeSpec
+ |ok error errorStringOrNil|
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ int __fd;
+ int __mode;
+ FILE *f;
+
+ ok = false;
+ if (strcmp(__stringVal(aMode), "w") == 0) {
+ __mode = O_WRONLY;
+ } else if (strcmp(__stringVal(aMode), "r") == 0) {
+ __mode = O_RDONLY;
+ } else {
+ __mode = O_RDWR;
+ }
+ do {
+ __BEGIN_INTERRUPTABLE__
+ __fd = open((char *) __stringVal(__INST(pathName)), __mode /* |O_NDELAY */);
+ __END_INTERRUPTABLE__
+ } while ((__fd < 0) && (errno == EINTR));
+
+ if (__fd >= 0) {
+ /*
+ * make it a FILE *
+ */
+ f = fdopen(__fd, __stringVal(aMode));
+ if (! f) {
+ error = __mkSmallInteger(errno);
+ __BEGIN_INTERRUPTABLE__
+ close(__fd);
+ __END_INTERRUPTABLE__
+ goto out;
+ }
+ setbuf(f, NULL);
+ __INST(buffered) = false;
+ __INST(handle) = __MKEXTERNALADDRESS(f);
+ __STORESELF(handle);
+
+#if defined(PCM_ENABLE_OUTPUT) && defined(PCM_ENABLE_INPUT)
+# if defined(SNDCTL_DSP_SETTRIGGER)
+ if (__mode == O_RDWR) {
+ int enable_bits = ~(PCM_ENABLE_OUTPUT|PCM_ENABLE_INPUT);
+
+ if (ioctl(__fd, SNDCTL_DSP_SETTRIGGER, &enable_bits) == -1)
+ {
+ console_fprintf(stderr, "can't request synchronous start of fullduplex operation");
+ }
+ }
+# endif
+#endif
+ ok = true;
+ } else {
+ error = __mkSmallInteger(errno);
+ }
+out:;
+#endif /* SUPPORT_DEV_AUDIO */
+%}.
+ ok == false ifTrue:[
+ lastErrorString := errorStringOrNil.
+ lastErrorNumber := error ? -1.
+ self openError:error.
+ "normally not reached"
+ ^ nil.
+ ].
+ self registerForFinalization.
+! !
+
+!SoundStream::DevAudio methodsFor:'private'!
+
+initialize
+ "initialize for least common mode"
+
+ super initialize.
+
+ OperatingSystem isUNIXlike ifTrue:[
+ '/dev/audio' asFilename exists ifTrue:[
+ "/
+ "/ sunos or linux
+ "/
+ pathName := '/dev/audio'.
+ ].
+ ].
+!
+
+resetSoundCard
+ "debugging interface - reset the soundCard"
+
+ |fd|
+
+ fd := self fileDescriptorOrNil.
+ fd isNil ifTrue:[
+ self errorNotOpen.
+ ^ nil
+ ].
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ int f = __intVal(fd);
+
+ if (__isSmallInteger(fd)) {
+ int __dummy;
+# if defined(SNDCTL_DSP_RESET)
+ if (ioctl(f, SNDCTL_DSP_RESET, &__dummy) >= 0) {
+ RETURN (self);
+ }
+# endif
+ }
+#endif /* SUPPORT_DEV_AUDIO */
+%}.
+ ^ UnsupportedOperationSignal raise
+
+ "
+ self writing resetSoundCard; dumpSettings; close
+ "
+!
+
+setAudioFormat:aSymbol
+ "set the format of the audio data as specified by aSymbol.
+ Returns true if sucessful - may fail with some formats on many sound devices."
+
+ |fd ok|
+
+ audioFormat == aSymbol ifTrue:[^ self].
+ super setAudioFormat:aSymbol.
+
+ fd := self fileDescriptor.
+ fd isNil ifTrue:[
+ ^ self
+ ].
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ OBJ sym = aSymbol;
+
+ if (__isSmallInteger(fd)) {
+ int f = __intVal(fd);
+ int __fmt = 0, __fmtWant;
+ union {
+ unsigned short us;
+ unsigned char ub[2];
+ } u;
+
+ if (__isSymbol(sym)) {
+ if (sym == @symbol(U16)) {
+ u.us = 0x1234;
+ if (u.ub[0] == 0x34) {
+ /* printf("U16_LE\n"); */
+ sym = @symbol(U16_LE);
+ } else {
+ /* printf("U16_BE\n"); */
+ sym = @symbol(U16_BE);
+ }
+ } else if (sym == @symbol(S16)) {
+ u.us = 0x1234;
+ if (u.ub[0] == 0x34) {
+ /* printf("S16_LE\n"); */
+ sym = @symbol(S16_LE);
+ } else {
+ /* printf("S16_BE\n"); */
+ sym = @symbol(S16_BE);
+ }
+ }
+
+ if (0) {
+# ifdef AFMT_MU_LAW
+ } else if (sym == @symbol(MU_LAW)) {
+ __fmt = AFMT_MU_LAW;
+# endif
+# ifdef AFMT_A_LAW
+ } else if (sym == @symbol(A_LAW)) {
+ __fmt = AFMT_A_LAW;
+# endif
+# ifdef AFMT_IMA_ADPCM
+ } else if (sym == @symbol(IMA_ADPCM)) {
+ __fmt = AFMT_IMA_ADPCM;
+# endif
+# ifdef AFMT_U8
+ } else if (sym == @symbol(U8)) {
+ __fmt = AFMT_U8;
+# endif
+# ifdef AFMT_S8
+ } else if (sym == @symbol(S8)) {
+ __fmt = AFMT_S8;
+# endif
+# ifdef AFMT_U16_LE
+ } else if (sym == @symbol(U16_LE)) {
+ __fmt = AFMT_U16_LE;
+# endif
+# ifdef AFMT_U16_BE
+ } else if (sym == @symbol(U16_BE)) {
+ __fmt = AFMT_U16_BE;
+# endif
+# ifdef AFMT_S16_LE
+ } else if (sym == @symbol(S16_LE)) {
+ __fmt = AFMT_S16_LE;
+# endif
+# ifdef AFMT_S16_BE
+ } else if (sym == @symbol(S16_BE)) {
+ __fmt = AFMT_S16_BE;
+# endif
+# ifdef AFMT_MPEG
+ } else if (sym == @symbol(MPEG)) {
+ __fmt = AFMT_MPEG;
+# endif
+
+# ifdef AUDIO_FORMAT_LINEAR16BIT
+ } else if (sym == @symbol(S16)) {
+ __fmt = AUDIO_FORMAT_LINEAR16BIT;
+# endif
+# ifdef AUDIO_FORMAT_ULAW
+ } else if (sym == @symbol(MU_LAW)) {
+ __fmt = AUDIO_FORMAT_ULAW;
+# endif
+# ifdef AUDIO_FORMAT_ALAW
+ } else if (sym == @symbol(A_LAW)) {
+ __fmt = AUDIO_FORMAT_ALAW;
+# endif
+ } else {
+ console_fprintf(stderr, "bad format: %s\n", __stringVal(sym));
+ ok = false;
+ goto bad;
+ }
+ }
+
+# ifdef SNDCTL_DSP_SETFMT
+ __fmtWant = __fmt;
+
+ if (ioctl(f, SNDCTL_DSP_SETFMT, &__fmt) >= 0) {
+ if (__fmt == __fmtWant) {
+ __INST(audioFormat) = sym;
+ } else {
+ /* console_fprintf(stderr, "want: %x; got: %x\n", __fmtWant, __fmt); */
+ }
+ } else {
+ /* console_fprintf(stderr, "got err-ret from setFmp %x\n", __fmt); */
+ ok = false;
+ }
+# else
+# ifdef AUDIO_SET_DATA_FORMAT /* hpux */
+ if (ioctl (f, AUDIO_SET_DATA_FORMAT, __fmt)) {
+ /* console_fprintf(stderr, "got err-ret from AUDIO_SET_DATA_FORMAT\n"); */
+ }
+# endif
+# endif /* SNDCTL_DSP_SETFMT */
+
+bad: ;
+ }
+#endif /* SUPPORT_DEV_AUDIO */
+%}.
+
+ "
+ self writing dumpSettings; close
+ self writing setAudioFormat:#'MU_LAW'; close
+ self writing setAudioFormat:#'U8'; dumpSettings; close
+ self writing setAudioFormat:#'MPEG'; dumpSettings; close
+ "
+!
+
+setChannels:nChannels
+ "set the number of channels (1 -> mono; 2 -> stereo).
+ Returns true if sucessful - may fail with many sound devices."
+
+ |fd|
+
+ numberOfChannels := nChannels.
+
+ fd := self fileDescriptorOrNil.
+ fd isNil ifTrue:[
+ ^ self
+ ].
+%{
+
+#ifdef SUPPORT_DEV_AUDIO
+ if (__isSmallInteger(fd) && __isSmallInteger(nChannels)) {
+ int f = __intVal(fd);
+ int __nCh = __intVal(nChannels);
+
+# ifdef SNDCTL_DSP_STEREO
+ if ((__nCh == 1) || (__nCh == 2)) {
+ int __stereo = (__nCh == 2) ? 1 : 0;
+
+ if (ioctl(f, SNDCTL_DSP_STEREO, &__stereo) >= 0) {
+ if (__stereo == 0) {
+ __INST(numberOfChannels) = __MKSMALLINT(1);
+ } else {
+ __INST(numberOfChannels) = __MKSMALLINT(2);
+ }
+ RETURN (self);
+ }
+ }
+# else
+# ifdef SOUND_PCM_WRITE_CHANNELS
+ if (ioctl(f, SOUND_PCM_WRITE_CHANNELS, &__nCh) >= 0) {
+ RETURN (self);
+ }
+# else
+# ifdef AUDIO_SET_CHANNELS /* hpux */
+ if (ioctl (f, AUDIO_SET_CHANNELS, __nCh)) {
+ /* console_fprintf(stderr, "got err-ret from AUDIO_SET_CHANNELS\n"); */
+ RETURN (self);
+ }
+# endif
+# endif
+# endif
+ }
+#endif /* SUPPORT_DEV_AUDIO */
+%}.
+
+ "
+ self writing setChannels:2; dumpSettings; close
+ self writing setChannels:2; setSampleRate:10000; dumpSettings; close
+ self writing setChannels:2; setSampleRate:40000; dumpSettings; close
+ "
+!
+
+setFragmentSize:blockSize
+ "set the soundDriver's fragmentSize"
+
+ |fd|
+
+ fragmentSize := blockSize.
+
+ fd := self fileDescriptorOrNil.
+ fd isNil ifTrue:[
+ ^ self
+ ].
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ int f = __intVal(fd);
+ int __blockSize = __intVal(blockSize);
+
+ if (__isSmallInteger(fd) && __isSmallInteger(blockSize)) {
+ __blockSize = __intVal(blockSize);
+# if defined(SNDCTL_DSP_SETFRAGMENT)
+ if (ioctl(f, SNDCTL_DSP_SETFRAGMENT, &__blockSize) >= 0) {
+ RETURN (self);
+ }
+# endif
+# ifdef AUDIO_SET_CHANNELS /* hpux */
+ if (ioctl (f, AUDIO_SET_TXBUFSIZE, __blockSize)) {
+ /* console_fprintf(stderr, "got err-ret from AUDIO_SET_TXBUFSIZE\n"); */
+ RETURN (self);
+ }
+# endif
+ }
+#endif // SUPPORT_DEV_AUDIO
+%}.
+!
+
+setSampleRate:hz
+ "set the sample rate"
+
+ |fd|
+
+ sampleRate := hz.
+
+ fd := self fileDescriptorOrNil.
+ fd isNil ifTrue:[
+ ^ self
+ ].
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ int f = __intVal(fd);
+ int __rate = __intVal(hz);
+
+ if (__isSmallInteger(fd) && __isSmallInteger(hz)) {
+ int __rateWant = __rate;
+
+# if defined(SNDCTL_DSP_SPEED)
+ if (ioctl(f, SNDCTL_DSP_SPEED, &__rate) >= 0) {
+ if (__rate != __rateWant) {
+ console_fprintf(stderr, "SoundStream [warning]: actual rate is %d\n", __rate);
+ hz = __MKSMALLINT(__rate);
+ }
+ __INST(sampleRate) = hz;
+ RETURN (self);
+ }
+# else
+# if defined(SOUND_PCM_WRITE_RATE)
+ if (ioctl(f, SOUND_PCM_WRITE_RATE, &__rate) >= 0) {
+ if (__rate != __rateWant) {
+ console_fprintf(stderr, "SoundStream [warning]: actual rate is %d\n", __rate);
+ hz = __MKSMALLINT(__rate);
+ }
+ __INST(sampleRate) = hz;
+ RETURN (self);
+ }
+# else
+# ifdef AUDIO_SET_SAMPLE_RATE /* hpux */
+ if (ioctl (f, AUDIO_SET_SAMPLE_RATE, __rate)) {
+ /* console_fprintf(stderr, "got err-ret from AUDIO_SET_SAMPLE_RATE\n"); */
+ __INST(sampleRate) = hz;
+ RETURN (self);
+ }
+# endif
+# endif
+# endif
+ }
+#endif // SUPPORT_DEV_AUDIO
+%}.
+
+ "
+ self writing setSampleRate:10000; dumpSettings; close
+ self writing setSampleRate:1000; dumpSettings; close
+ self writing setSampleRate:8000; dumpSettings; close
+ "
+! !
+
+!SoundStream::DevAudio methodsFor:'queries'!
+
+supportedAudioFormats
+ "return a collection of supported audio formats.
+ possibly returned symbols are:
+ U8 unsigned 8bit samples
+ S8 signed 8bit samples
+ U16 unsigned 16bit samples in native format
+ U16_LE unsigned 16bit big endian samples
+ U16_BE unsigned 16bit big endian samples
+ S16 signed 16bit little endian samples in native format
+ S16_LE signed 16bit little endian samples
+ S16_BE signed 16bit big endian samples
+ S24 signed 24bit little endian samples in native format
+ S24_LE signed 24bit little endian samples
+ S24_BE signed 24bit big endian samples
+ S32 signed 32bit little endian samples in native format
+ S32_LE signed 32bit little endian samples
+ S32_BE signed 32bit big endian samples
+ F32 float samples
+ MPEG audio mpeg encoded
+ MU_LAW u-law encoded 8bit samples
+ A_LAW a-law encoded 8bit samples
+ IMA_ADPCM adpcm encoded
+ the set of returned symbols depends on the underlying sound hardware.
+ "
+
+ |fd audioFormatMask
+ supportedFormats
+ supports_MU_LAW supports_A_LAW supports_MPEG
+ supports_IMA_ADPCM
+ supports_S8 supports_U8
+ supports_S16_LE supports_S16_BE
+ supports_U16_LE supports_U16_BE
+ supports_U32_LE supports_U32_BE
+ supports_S24_LE supports_S24_BE
+ supports_S32_LE supports_S32_BE
+ supports_F32
+ |
+
+ fd := self fileDescriptorOrNil.
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ supports_MU_LAW = true;
+
+ if (fd != nil) {
+ int f = __intVal(fd);
+ int __audioFormatMask = 0;
+
+# if defined(SNDCTL_DSP_GETFMTS)
+ if (ioctl(f, SNDCTL_DSP_GETFMTS, &__audioFormatMask) >= 0) {
+ audioFormatMask = __MKSMALLINT(__audioFormatMask);
+
+# ifdef AFMT_MU_LAW
+ supports_MU_LAW = (__audioFormatMask & AFMT_MU_LAW) ? true : false;
+# endif
+# ifdef AFMT_A_LAW
+ supports_A_LAW = (__audioFormatMask & AFMT_A_LAW) ? true : false;
+# endif
+# ifdef AFMT_IMA_ADPCM
+ supports_IMA_ADPCM = (__audioFormatMask & AFMT_IMA_ADPCM) ? true : false;
+# endif
+# ifdef AFMT_U8
+ supports_U8 = (__audioFormatMask & AFMT_U8) ? true : false;
+# endif
+# ifdef AFMT_S16_LE
+ supports_S16_LE = (__audioFormatMask & AFMT_S16_LE) ? true : false;
+# endif
+# ifdef AFMT_S16_BE
+ supports_S16_BE = (__audioFormatMask & AFMT_S16_BE) ? true : false;
+# endif
+# ifdef AFMT_S8
+ supports_S8 = (__audioFormatMask & AFMT_S8) ? true : false;
+# endif
+# ifdef AFMT_U16_LE
+ supports_U16_LE = (__audioFormatMask & AFMT_U16_LE) ? true : false;
+# endif
+# ifdef AFMT_U16_BE
+ supports_U16_BE = (__audioFormatMask & AFMT_U16_BE) ? true : false;
+# endif
+# ifdef AFMT_MPEG
+ supports_MPEG = (__audioFormatMask & AFMT_MPEG) ? true : false;
+# endif
+ }
+ }
+# endif // SNDCTL_DSP_GETFMTS
+
+# ifdef hpux
+ supports_A_LAW = true;
+ supports_S16_BE = true;
+# endif
+#endif /* SUPPORT_DEV_AUDIO */
+%}.
+ supportedFormats := IdentitySet new.
+ (supports_MU_LAW ? false) ifTrue:[
+ supportedFormats add:#'MU_LAW'
+ ].
+ (supports_A_LAW ? false) ifTrue:[
+ supportedFormats add:#'A_LAW'
+ ].
+ (supports_IMA_ADPCM ? false) ifTrue:[
+ supportedFormats add:#'IMA_ADPCM'
+ ].
+ (supports_MPEG ? false) ifTrue:[
+ supportedFormats add:#'MPEG'
+ ].
+ (supports_S8 ? false) ifTrue:[
+ supportedFormats add:#'S8'
+ ].
+ (supports_U8 ? false) ifTrue:[
+ supportedFormats add:#'U8'
+ ].
+ (supports_S16_LE ? false) ifTrue:[
+ supportedFormats add:#'S16_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'S16'.
+ ]
+ ].
+ (supports_S16_BE ? false) ifTrue:[
+ supportedFormats add:#'S16_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'S16'.
+ ]
+ ].
+ (supports_S24_LE ? false) ifTrue:[
+ supportedFormats add:#'S24_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'S24'.
+ ]
+ ].
+ (supports_S24_BE ? false) ifTrue:[
+ supportedFormats add:#'S24_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'S24'.
+ ]
+ ].
+ (supports_S32_LE ? false) ifTrue:[
+ supportedFormats add:#'S32_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'S32'.
+ ]
+ ].
+ (supports_S32_BE ? false) ifTrue:[
+ supportedFormats add:#'S32_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'S32'.
+ ]
+ ].
+ (supports_U16_LE ? false) ifTrue:[
+ supportedFormats add:#'U16_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'U16'.
+ ]
+ ].
+ (supports_U16_BE ? false) ifTrue:[
+ supportedFormats add:#'U16_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'U16'.
+ ]
+ ].
+ (supports_F32 ? false) ifTrue:[
+ supportedFormats add:#'F32'
+ ].
+ ^ supportedFormats.
+
+ "
+ |s formats|
+
+ s := self writing.
+ formats := s supportedAudioFormats.
+ s close.
+ formats
+ "
+! !
+
+!SoundStream::DevAudio methodsFor:'reading'!
+
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with ByteArrays only."
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ /*
+ * redefined to work around a bug in the linux sound driver;
+ * if a read is interrupted (EINTR), it is not defined, how many
+ * bytes have been read from the device.
+ *
+ * As a workaround, disable signals here to prevent being interrupted.
+ */
+ int cnt, offs, objSize, n;
+ char *cp;
+ OBJ fp;
+ FILE *f;
+ int fd;
+
+ if ((fp = __INST(handle)) != nil) {
+ f = __FILEVal(fp);
+ if (__INST(mode) != @symbol(writeonly)) {
+ if (__bothSmallInteger(count, start)) {
+ cnt = __intVal(count);
+ offs = __intVal(start) - 1;
+
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ do {
+ cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
+
+ n = cnt;
+# ifdef LINUX
+ sigsetmask(~0);
+# endif
+ if (__INST(buffered) == true) {
+ n = fread(cp, 1, n, f);
+ } else {
+ fd = fileno(f);
+ n = read(fd, cp, n);
+ }
+# ifdef LINUX
+ sigsetmask(0);
+# endif
+ __BEGIN_INTERRUPTABLE__
+ __END_INTERRUPTABLE__
+ // console_fprintf(stderr, "SoundStream: read %d bytes\n", n);
+ if (n > 0) {
+ offs += n;
+ cnt -= n;
+ } else {
+ if (n < 0) {
+ console_fprintf(stderr, "read error: %d\n", __threadErrno);
+ RETURN (count);
+ }
+ }
+ } while (cnt);
+ }
+ RETURN (count);
+ }
+ }
+ }
+#endif /* SUPPORT_DEV_AUDIO */
+
+%}.
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::DevAudio methodsFor:'writing'!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Redefined, since IRIS audio library cannot be used with stdio.
+ (at least I don't know). Use with ByteArrays only."
+
+%{
+#ifdef SUPPORT_DEV_AUDIO
+ /*
+ * redefine to work around a bug in the linux sound driver;
+ * if a write is interrupted (EINTR), it is not defined, how many
+ * bytes have been written to the device.
+ * I.e. a retry of the write may lead to ever-playing without ever
+ * finishing.
+ *
+ * As a workaround, disable signals here to prevent the write from
+ * being interrupted.
+ */
+ int cnt, offs, objSize, n;
+ char *cp;
+ OBJ fp;
+ FILE *f;
+ int fd;
+
+ if ((fp = __INST(handle)) != nil) {
+ f = __FILEVal(fp);
+ if (__INST(mode) != @symbol(readonly)) {
+ if (__bothSmallInteger(count, start)) {
+ cnt = __intVal(count);
+ offs = __intVal(start) - 1;
+
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ do {
+ cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
+
+ n = cnt;
+# ifdef LINUX
+ sigsetmask(~0);
+# endif
+ if (__INST(buffered) == true) {
+ n = fwrite(cp, 1, n, f);
+ } else {
+ fd = fileno(f);
+ n = write(fd, cp, n);
+ }
+# ifdef LINUX
+ sigsetmask(0);
+# endif
+ __BEGIN_INTERRUPTABLE__
+ __END_INTERRUPTABLE__
+ if (n > 0) {
+ offs += n;
+ cnt -= n;
+ } else {
+ if (n < 0) {
+ console_fprintf(stderr, "write error: %d\n", __threadErrno);
+ RETURN (count);
+ }
+ }
+ } while (cnt);
+ }
+ RETURN (count);
+ }
+ }
+ }
+#endif /* SUPPORT_DEV_AUDIO */
+
+%}.
+ ^ self errorUnsupportedOperation
+! !
+
+!SoundStream::IRISAudio class methodsFor:'documentation'!
+
+documentation
+"
+ documentation to be added.
+
+ class:
+ <a short class summary here, describing what instances represent>
+
+ responsibilities:
+ <describing what my main role is>
+
+ collaborators:
+ <describing with whom and how I talk to>
+
+ API:
+ <public api and main messages>
+
+ example:
+ <a one-line examples on how to use - can also be in a separate example method>
+
+ implementation:
+ <implementation points>
+
+ [author:]
+ exept MBP
+
+ [instance variables:]
+
+ [class variables:]
+
+ [see also:]
+
+"
+! !
+
+!SoundStream::IRISAudio class methodsFor:'queries'!
+
+isSupported
+%{
+#ifdef SUPPORT_IRIS_AUDIO
+ RETURN(true);
+#endif
+%}.
+ ^ false.
+! !
+
+!SoundStream::IRISAudio methodsFor:'misc'!
+
+flush
+ "wait until all sound has been played"
+
+%{
+#ifdef SUPPORT_IRIS_AUDIO
+ OPJ port;
+ ALport p;
+
+ if ((port = __INST(alPort)) != nil) {
+ p = __ALportVal(port);
+ while (ALgetfilled(p) > 0) {
+ sginap(1);
+ }
+ }
+ RETURN(self);
+#endif /* SUPPORT_IRIS_AUDIO */
+%}.
+
+! !
+
+!SoundStream::IRISAudio methodsFor:'open & close'!
+
+closeFile
+ "a stream has been collected - close the file"
+
+%{
+#ifdef SUPPORT_IRIS_AUDIO
+ OBJ port;
+
+ if ((port = __INST(alPort)) != nil) {
+ __INST(alPort) = nil;
+ ALcloseport(__ALportVal(port));
+ }
+ RETURN (self);
+#endif /* SUPPORT_IRIS_AUDIO */
+%}.
+
+!
+
+openWithMode:aMode attributes:attributeSpec
+ |ok error errorStringOrNil|
+
+%{
+#ifdef SUPPORT_IRIS_AUDIO
+ ALconfig config;
+ ALport p;
+ long params[] = {
+ AL_INPUT_SOURCE, AL_INPUT_MIC,
+ AL_INPUT_RATE, 8000,
+ AL_OUTPUT_RATE, 8000,
+ };
+
+ ok = false.
+ config = ALnewconfig();
+ if (__INST(numberOfChannels) == __MKSMALLINT(2))
+ ALsetchannels(config, AL_STEREO);
+ else
+ ALsetchannels(config, AL_MONO);
+ if (__INST(bitsPerSample) == __MKSMALLINT(16))
+ ALsetwidth(config, AL_SAMPLE_16);
+ else
+ ALsetwidth(config, AL_SAMPLE_8);
+
+ if (__isSmallInteger(__INST(sampleRate)))
+ params[3] = params[5] = __intVal(__INST(sampleRate));
+
+ ALsetparams(AL_DEFAULT_DEVICE, params, 6);
+ p = ALopenport("smallchat", (char *)_stringVal(aMode), config);
+ if (p) {
+ OBJ t;
+
+ t = __MKEXTERNALADDRESS(p); __INST(alPort) = t; __STORE(self, t);
+ } else {
+ __INST(alPort) = nil;
+ goto out;
+ }
+ __INST(binary) = true;
+
+ ALfreeconfig(config);
+ /*
+ * get the parameters actually installed
+ */
+ config = ALgetconfig(p);
+ switch (ALgetchannels(config)) {
+ default:
+ /* cannot happen */
+ case AL_MONO:
+ __INST(numberOfChannels) = __MKSMALLINT(1);
+ break;
+ case AL_STEREO:
+ __INST(numberOfChannels) = __MKSMALLINT(2);
+ break;
+ }
+ switch (ALgetwidth(config)) {
+ default:
+ /* cannot happen */
+ case AL_SAMPLE_8:
+ __INST(bitsPerSample) = __MKSMALLINT(8);
+ break;
+ case AL_SAMPLE_16:
+ __INST(bitsPerSample) = __MKSMALLINT(16);
+ break;
+ case AL_SAMPLE_24:
+ __INST(bitsPerSample) = __MKSMALLINT(24);
+ break;
+ }
+ ALgetparams(AL_DEFAULT_DEVICE, params, 6);
+ __INST(sampleRate) = __MKSMALLINT(params[3]);
+
+ ALfreeconfig(config);
+ ok = true;
+out:;
+#endif /* SUPPORT_IRIS_AUDIO */
+
+%}.
+ ok == false ifTrue:[
+ lastErrorString := errorStringOrNil.
+ lastErrorNumber := error ? -1.
+ self openError:error.
+ "normally not reached"
+ ^ nil.
+ ].
+ self registerForFinalization.
+! !
+
+!SoundStream::IRISAudio methodsFor:'queries'!
+
+supportedAudioFormats
+ "return a collection of supported audio formats.
+ possibly returned symbols are:
+ U8 unsigned 8bit samples
+ S8 signed 8bit samples
+ U16 unsigned 16bit samples in native format
+ U16_LE unsigned 16bit big endian samples
+ U16_BE unsigned 16bit big endian samples
+ S16 signed 16bit little endian samples in native format
+ S16_LE signed 16bit little endian samples
+ S16_BE signed 16bit big endian samples
+ S24 signed 24bit little endian samples in native format
+ S24_LE signed 24bit little endian samples
+ S24_BE signed 24bit big endian samples
+ S32 signed 32bit little endian samples in native format
+ S32_LE signed 32bit little endian samples
+ S32_BE signed 32bit big endian samples
+ F32 float samples
+ MPEG audio mpeg encoded
+ MU_LAW u-law encoded 8bit samples
+ A_LAW a-law encoded 8bit samples
+ IMA_ADPCM adpcm encoded
+ the set of returned symbols depends on the underlying sound hardware.
+ "
+
+ ^ #( U8 U16_BE U16 )
+
+ "
+ |s formats|
+
+ s := self writing.
+ formats := s supportedAudioFormats.
+ s close.
+ formats
+ "
+! !
+
+!SoundStream::IRISAudio methodsFor:'reading'!
+
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with ByteArrays only."
+
+%{
+#ifdef SUPPORT_IRIS_AUDIO
+ {
+ OBJ port;
+ ALport p;
+ int cnt, offs, nSamp;
+ int objSize;
+ char *cp;
+
+ if ((port = __INST(alPort)) != nil) {
+ if (__INST(mode) != _writeonly) {
+ if (__bothSmallInteger(count, start)) {
+ cnt = __intVal(count);
+ offs = __intVal(start) - 1;
+ p = __ALportVal(port);
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
+ cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
+ if (__INST(bitsPerSample) == __MKSMALLINT(16))
+ nSamp = cnt / 2;
+ else
+ nSamp = cnt;
+ ALreadsamps(p, cp, nSamp);
+ RETURN ( __MKSMALLINT(cnt) );
+ }
+ }
+ }
+ }
+ }
+#endif /* SUPPORT_IRIS_AUDIO */
+
+%}.
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::IRISAudio methodsFor:'writing'!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Redefined, since IRIS audio library cannot be used with stdio.
+ (at least I don't know). Use with ByteArrays only."
+
+%{
+#ifdef SUPPORT_IRIS_AUDIO
+ {
+ OBJ port;
+ ALport p;
+ int cnt, offs, nSamp;
+ int objSize;
+ char *cp;
+
+ if ((port = __INST(alPort)) != nil) {
+ if (__INST(mode) != @symbol(readonly)) {
+ if (__bothSmallInteger(count, start)) {
+ cnt = __intVal(count);
+ offs = __intVal(start) - 1;
+ p = __ALportVal(port);
+
+ /*
+ * compute number of samples
+ */
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ cp = (char *)__InstPtr(anObject) + OHDR_SIZE + offs;
+ if (__INST(bitsPerSample) == __MKSMALLINT(16))
+ nSamp = cnt / 2;
+ else
+ nSamp = cnt;
+ ALwritesamps(p, cp, cnt);
+ RETURN ( count );
+ }
+ }
+ }
+ }
+ }
+#endif /* SUPPORT_IRIS_AUDIO */
+%}.
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::PortAudio class methodsFor:'default values'!
+
+defaultSampleRate
+ "minimum, supported by all audio systems"
+
+%{
+#ifdef SUPPORT_PORTAUDIO
+ RETURN (__MKSMALLINT(PORTAUDIO_DEFAULT_FREQ));
+#endif
+%}.
+
+ ^ 8000
+! !
+
+!SoundStream::PortAudio class methodsFor:'documentation'!
+
+documentation
+"
+ documentation to be added.
+
+ class:
+ <a short class summary here, describing what instances represent>
+
+ responsibilities:
+ <describing what my main role is>
+
+ collaborators:
+ <describing with whom and how I talk to>
+
+ API:
+ <public api and main messages>
+
+ example:
+ <a one-line examples on how to use - can also be in a separate example method>
+
+ implementation:
+ <implementation points>
+
+ [author:]
+ exept MBP
+
+ [instance variables:]
+
+ [class variables:]
+
+ [see also:]
+
+"
+! !
+
+!SoundStream::PortAudio class methodsFor:'initialization'!
+
+primitiveInitializeDevice
+ |errorMessageOrNil|
+
+%{
+#ifdef SUPPORT_PORTAUDIO
+ PaError paErr = 0;
+
+ if (DEBUGGING) {
+ fprintf(stderr, "calling Pa_Initialize...\n");
+ }
+ if ((paErr = Pa_Initialize()) != paNoError ) {
+ errorMessageOrNil = __MKSTRING(Pa_GetErrorText( paErr ) );
+ fprintf(stderr, "SoundStream [error]: Pa_Initialize failed\n");
+ };
+#endif
+%}.
+ errorMessageOrNil notNil ifTrue:[
+ self error:'failed to initialize audio device: ',errorMessageOrNil
+ ].
+
+! !
+
+!SoundStream::PortAudio class methodsFor:'queries'!
+
+isSupported
+%{
+#ifdef SUPPORT_PORTAUDIO
+ RETURN(true);
+#endif
+%}.
+ ^ false.
+! !
+
+!SoundStream::PortAudio methodsFor:'misc'!
+
+flush
+ "wait until all sound has been played"
+
+%{
+#ifdef SUPPORT_PORTAUDIO
+ OBJ str;
+ if ((str = __INST(handle1)) != nil) {
+ struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
+
+ while (paStreamData->currentBuffer != NULL) {
+ struct timespec a;
+ a.tv_sec = 0;
+ a.tv_nsec = 1 * 1000000; // 1 milliseconds asNanoseconds -> 1000000
+ nanosleep( &a, NULL );
+ }
+ }
+#endif /* SUPPORT_PORTAUDIO */
+%}.
+
+! !
+
+!SoundStream::PortAudio methodsFor:'open & close'!
+
+closeFile
+ "a stream has been collected - close the file"
+
+%{
+#ifdef SUPPORT_PORTAUDIO
+ OBJ str;
+ if ((str = __INST(handle1)) != nil) {
+ struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
+ struct paBuffer* buffer;
+
+ if (DEBUGGING) {
+ fprintf(stderr, "pa close\n");
+ }
+ __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;
+ while (buffer != NULL) {
+ struct paBuffer* nextBuffer = buffer->nextBuffer;
+ free(buffer->sampleData);
+ free(buffer);
+ buffer = nextBuffer;
+ }
+ buffer = paStreamData->freeList;
+ paStreamData->freeList = NULL;
+ while (buffer != NULL) {
+ struct paBuffer* nextBuffer = buffer->nextBuffer;
+ free(buffer->sampleData);
+ free(buffer);
+ buffer = nextBuffer;
+ }
+
+ UNLOCK(paStreamData->lock);
+ RELEASELOCK(paStreamData->lock);
+ free(paStreamData);
+ }
+ RETURN (self);
+#endif // SUPPORT_PORTAUDIO
+%}.
+!
+
+openWithMode:aMode attributes:attributeSpec
+ |ok error errorStringOrNil|
+
+%{
+#ifdef SUPPORT_PORTAUDIO
+ static PaStreamParameters outputParameters;
+ PaStream *stream;
+ PaError paErr;
+ struct paStreamData* paStreamData;
+ int nChannels, sampleRate, bytesPerSample;
+# define FRAMES_PER_BUFFER 128
+
+ ok = false;
+
+ /* default output device */
+ outputParameters.device = Pa_GetDefaultOutputDevice();
+ if (outputParameters.device == paNoDevice) {
+ fprintf(stderr, "SoundStream [warning]: No default output device.\n");
+ errorStringOrNil = __MKSTRING("No default output device");
+ goto out;
+ }
+
+ if (__isSmallInteger(__INST(numberOfChannels))) {
+ nChannels = __intVal(__INST(numberOfChannels));
+ } else {
+ nChannels = 1;
+ }
+ outputParameters.channelCount = nChannels;
+ outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
+ outputParameters.hostApiSpecificStreamInfo = NULL;
+
+ // The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8
+ // and aUInt8 are usually implemented by all implementations.
+ // The floating point representation (paFloat32) uses +1.0 and -1.0 as the
+ // maximum and minimum respectively.
+ // paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
+
+ if (__INST(audioFormat) == @symbol(S16)) {
+ outputParameters.sampleFormat = paInt16;
+ bytesPerSample = 2;
+ } else if (__INST(audioFormat) == @symbol(S32)) {
+ outputParameters.sampleFormat = paInt32;
+ bytesPerSample = 4;
+ } else if (__INST(audioFormat) == @symbol(S24)) {
+ outputParameters.sampleFormat = paInt24;
+ bytesPerSample = 3;
+ } else if (__INST(audioFormat) == @symbol(S8)) {
+ outputParameters.sampleFormat = paInt8;
+ bytesPerSample = 1;
+ } else if (__INST(audioFormat) == @symbol(F32)) {
+ outputParameters.sampleFormat = paFloat32;
+ bytesPerSample = 4;
+ } else if (__INST(audioFormat) == @symbol(U8)) {
+ outputParameters.sampleFormat = paUInt8;
+ bytesPerSample = 1;
+ } else {
+ fprintf(stderr, "SoundStream [warning]: unknown format - using U8\n");
+ outputParameters.sampleFormat = paUInt8;
+ bytesPerSample = 1;
+ }
+
+ if (__isSmallInteger(__INST(sampleRate))) {
+ sampleRate = __intVal(__INST(sampleRate));
+ } else {
+ fprintf(stderr, "SoundStream [warning]: using default sampleRate 8000\n");
+ sampleRate = 8000;
+ }
+
+ paStreamData = (struct paStreamData*)malloc(sizeof(struct paStreamData));
+ if (paStreamData == NULL) {
+ fprintf(stderr, "SoundStream [warning]: failed to allocate paStream\n");
+ errorStringOrNil = __MKSTRING("failed to allocate paStream");
+ goto out;
+ }
+
+ paErr = Pa_OpenStream(
+ &stream,
+ NULL, /* no input */
+ &outputParameters,
+ sampleRate,
+ FRAMES_PER_BUFFER,
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ paCallback,
+ paStreamData );
+
+ if (paErr != paNoError) {
+ fprintf(stderr, "SoundStream [warning]: openStream: %s\n", Pa_GetErrorText( paErr ));
+ free(paStreamData);
+ 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;
+
+ {
+ OBJ t;
+ t = __MKEXTERNALADDRESS(paStreamData); __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_PORTAUDIO */
+
+%}.
+ ok == false ifTrue:[
+ lastErrorString := errorStringOrNil.
+ lastErrorNumber := error ? -1.
+ self openError:error.
+ "normally not reached"
+ ^ nil.
+ ].
+ self registerForFinalization.
+
+! !
+
+!SoundStream::PortAudio methodsFor:'private'!
+
+initialize
+ "initialize for least common mode"
+
+ super initialize.
+
+ (IsInitialized ? false) ifFalse:[
+ self class primitiveInitializeDevice
+ ].
+
+ "Created: 17.11.1995 / 17:28:14 / cg"
+! !
+
+!SoundStream::PortAudio methodsFor:'queries'!
+
+supportedAudioFormats
+ "return a collection of supported audio formats.
+ possibly returned symbols are:
+ U8 unsigned 8bit samples
+ S8 signed 8bit samples
+ U16 unsigned 16bit samples in native format
+ U16_LE unsigned 16bit big endian samples
+ U16_BE unsigned 16bit big endian samples
+ S16 signed 16bit little endian samples in native format
+ S16_LE signed 16bit little endian samples
+ S16_BE signed 16bit big endian samples
+ S24 signed 24bit little endian samples in native format
+ S24_LE signed 24bit little endian samples
+ S24_BE signed 24bit big endian samples
+ S32 signed 32bit little endian samples in native format
+ S32_LE signed 32bit little endian samples
+ S32_BE signed 32bit big endian samples
+ F32 float samples
+ MPEG audio mpeg encoded
+ MU_LAW u-law encoded 8bit samples
+ A_LAW a-law encoded 8bit samples
+ IMA_ADPCM adpcm encoded
+ the set of returned symbols depends on the underlying sound hardware.
+ "
+
+ |fd audioFormatMask
+ supportedFormats
+ supports_MU_LAW supports_A_LAW supports_MPEG
+ supports_IMA_ADPCM
+ supports_S8 supports_U8
+ supports_S16_LE supports_S16_BE
+ supports_U16_LE supports_U16_BE
+ supports_U32_LE supports_U32_BE
+ supports_S24_LE supports_S24_BE
+ supports_S32_LE supports_S32_BE
+ supports_F32
+ |
+
+%{
+#ifdef SUPPORT_PORTAUDIO
+ supports_U8 = true;
+ supports_S8 = true;
+ supports_S16_LE = true;
+ supports_S24_LE = true;
+ supports_S32_LE = true;
+ supports_F32 = true;
+#endif
+%}.
+ supportedFormats := IdentitySet new.
+ (supports_MU_LAW ? false) ifTrue:[
+ supportedFormats add:#'MU_LAW'
+ ].
+ (supports_A_LAW ? false) ifTrue:[
+ supportedFormats add:#'A_LAW'
+ ].
+ (supports_IMA_ADPCM ? false) ifTrue:[
+ supportedFormats add:#'IMA_ADPCM'
+ ].
+ (supports_MPEG ? false) ifTrue:[
+ supportedFormats add:#'MPEG'
+ ].
+ (supports_S8 ? false) ifTrue:[
+ supportedFormats add:#'S8'
+ ].
+ (supports_U8 ? false) ifTrue:[
+ supportedFormats add:#'U8'
+ ].
+ (supports_S16_LE ? false) ifTrue:[
+ supportedFormats add:#'S16_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'S16'.
+ ]
+ ].
+ (supports_S16_BE ? false) ifTrue:[
+ supportedFormats add:#'S16_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'S16'.
+ ]
+ ].
+ (supports_S24_LE ? false) ifTrue:[
+ supportedFormats add:#'S24_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'S24'.
+ ]
+ ].
+ (supports_S24_BE ? false) ifTrue:[
+ supportedFormats add:#'S24_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'S24'.
+ ]
+ ].
+ (supports_S32_LE ? false) ifTrue:[
+ supportedFormats add:#'S32_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'S32'.
+ ]
+ ].
+ (supports_S32_BE ? false) ifTrue:[
+ supportedFormats add:#'S32_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'S32'.
+ ]
+ ].
+ (supports_U16_LE ? false) ifTrue:[
+ supportedFormats add:#'U16_LE'.
+ UninterpretedBytes isBigEndian ifFalse:[
+ supportedFormats add:#'U16'.
+ ]
+ ].
+ (supports_U16_BE ? false) ifTrue:[
+ supportedFormats add:#'U16_BE'.
+ UninterpretedBytes isBigEndian ifTrue:[
+ supportedFormats add:#'U16'.
+ ]
+ ].
+ (supports_F32 ? false) ifTrue:[
+ supportedFormats add:#'F32'
+ ].
+ ^ supportedFormats.
+
+ "
+ |s formats|
+
+ s := self writing.
+ formats := s supportedAudioFormats.
+ s close.
+ formats
+ "
+! !
+
+!SoundStream::PortAudio methodsFor:'reading'!
+
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with ByteArrays only."
+
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::PortAudio methodsFor:'writing'!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Redefined, since IRIS audio library cannot be used with stdio.
+ (at least I don't know). Use with ByteArrays only."
+
+%{
+#ifdef SUPPORT_PORTAUDIO
+ OBJ str;
+
+ if ((str = __INST(handle1)) != nil) {
+ if (__INST(mode) != @symbol(readonly)) {
+ if (__bothSmallInteger(count, start)) {
+ // allocate a buffer
+ struct paStreamData* paStreamData = (struct paStreamData*)__externalAddressVal(str);
+ int cnt = __intVal(count);
+ int offs = __intVal(start) - 1;
+ int objSize;
+
+ objSize = _Size(anObject) - OHDR_SIZE;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ struct paBuffer* newBuffer;
+ struct paBuffer* toFree;
+ int mustStart = 0;
+ unsigned char* newSampleData;
+
+ // try freeList
+ LOCK(paStreamData->lock);
+ if ((paStreamData->freeList != NULL)
+ && (paStreamData->freeList->bufferSize == cnt)) {
+ // reuse
+ newBuffer = paStreamData->freeList;
+ paStreamData->freeList = newBuffer->nextBuffer;
+ newSampleData = newBuffer->sampleData;
+ toFree = NULL;
+ if (DEBUGGING) {
+ fprintf(stderr, "from free: %p (->%p)\n", newBuffer, newBuffer->sampleData);
+ }
+ } else {
+ // free them all
+ toFree = paStreamData->freeList;
+ paStreamData->freeList = NULL;
+ newBuffer = malloc(sizeof(struct paBuffer));
+ newSampleData = malloc(cnt);
+ newBuffer->sampleData = newSampleData;
+ newBuffer->bufferSize = cnt;
+ if (DEBUGGING) {
+ fprintf(stderr, "alloc: %p (->%p)\n", newBuffer, newBuffer->sampleData);
+ }
+ }
+
+ memcpy(newSampleData, (__ByteArrayInstPtr(anObject)->ba_element)+offs, cnt);
+ newBuffer->nextBuffer = NULL;
+
+ if (paStreamData->lastBuffer == NULL) {
+ // start stream's buffer list
+ paStreamData->currentBuffer = newBuffer;
+ mustStart = 1;
+ } else {
+ // append to stream's buffer list
+ paStreamData->lastBuffer->nextBuffer = newBuffer;
+ }
+ paStreamData->lastBuffer = newBuffer;
+ paStreamData->hasFinished = 0;
+ UNLOCK(paStreamData->lock);
+
+ if (mustStart) {
+ PaError paErr = Pa_StartStream( paStreamData->stream );
+ if ( paErr != paNoError ) {
+ if (DEBUGGING) {
+ fprintf(stderr, "start error\n");
+ }
+ RETURN (0);
+ };
+ }
+ RETURN (count);
+ }
+ }
+ }
+ }
+#endif /* SUPPORT_PORTAUDIO */
+
+%}.
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::Win32DirectSound class methodsFor:'documentation'!
+
+documentation
+"
+ documentation to be added.
+
+ class:
+ <a short class summary here, describing what instances represent>
+
+ responsibilities:
+ <describing what my main role is>
+
+ collaborators:
+ <describing with whom and how I talk to>
+
+ API:
+ <public api and main messages>
+
+ example:
+ <a one-line examples on how to use - can also be in a separate example method>
+
+ implementation:
+ <implementation points>
+
+ [author:]
+ exept MBP
+
+ [instance variables:]
+
+ [class variables:]
+
+ [see also:]
+
+"
+! !
+
+!SoundStream::Win32DirectSound class methodsFor:'queries'!
+
+isSupported
+%{
+#ifdef SUPPORT_WIN32_DIRECTSOUND
+ RETURN(true);
+#endif
+%}.
+ ^ false.
+! !
+
+!SoundStream::Win32DirectSound methodsFor:'misc'!
+
+flush
+ "wait until all sound has been played"
+
+ "/ as yet unimplemented
+ ^ self
+! !
+
+!SoundStream::Win32DirectSound methodsFor:'open & close'!
+
+closeFile
+ "a stream has been collected - close the file"
+
+%{
+#ifdef SUPPORT_WIN32_DIRECTSOUND
+ OBJ oDirectSound, oDSBuffer;
+ LPDIRECTSOUND t_pDirectSound;
+ LPDIRECTSOUNDBUFFER t_pDSBuffer;
+
+ if ((oDSBuffer = __INST(pDSBuffer)) != nil) {
+ __INST(pDSBuffer) = nil;
+ t_pDSBuffer = __DSBufferVal(oDSBuffer);
+ if (t_pDSBuffer) {
+ IDirectSoundBuffer_Stop(t_pDSBuffer);
+ IDirectSoundBuffer_Release(t_pDSBuffer);
+ }
+ }
+ if ((oDirectSound = __INST(pDirectSound)) != nil) {
+ __INST(pDirectSound) = nil;
+ t_pDirectSound = __DirectSoundVal(oDirectSound);
+ if (t_pDirectSound) {
+ IDirectSound_Release(t_pDirectSound);
+ }
+ }
+ RETURN (self);
+#endif /* SUPPORT_WIN32_DIRECTSOUND */
+%}.
+
+!
+
+openWithMode:aMode attributes:attributeSpec
+ |ok error errorStringOrNil|
+
+%{
+#ifdef SUPPORT_WIN32_DIRECTSOUND
+ HRESULT result;
+ LPDIRECTSOUND t_pDirectSound;
+ LPDIRECTSOUNDBUFFER t_pDSBuffer, t_pDSPrimeBuffer;
+ WAVEFORMATEX wfFormat;
+ DSBUFFERDESC dsbdDesc, primarydsbDesc;
+ BYTE *pDSBuffData;
+ int t_cbBufSize;
+ int dwDataLen;
+
+ ok = false;
+
+ /* Create the DS object */
+ if ((result = DirectSoundCreate(NULL, &t_pDirectSound, NULL)) != DS_OK) {
+ console_fprintf(stderr,"SoundStream: Cannot open default sound device!!\n");
+ goto out;
+ }
+
+ /* Define the wave format structure */
+ wfFormat.wFormatTag = WAVE_FORMAT_PCM;
+ wfFormat.nChannels = __intVal(__INST(numberOfChannels));
+ wfFormat.nSamplesPerSec = __intVal(__INST(sampleRate));
+ wfFormat.wBitsPerSample = __intVal(__INST(bitsPerSample));
+ wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8;
+ wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign;
+ wfFormat.cbSize = 0;
+
+# if 0
+ /* Setup the primary DS buffer description */
+ ZeroMemory(&primarydsbDesc, sizeof(DSBUFFERDESC));
+ primarydsbDesc.dwSize = sizeof(DSBUFFERDESC);
+ primarydsbDesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
+ primarydsbDesc.dwBufferBytes = 0;
+ primarydsbDesc.lpwfxFormat = NULL;
+
+ /* Create the primary DS buffer */
+ if ((result = IDirectSound_CreateSoundBuffer(t_pDirectSound, &primarydsbDesc,
+ &t_pDSPrimeBuffer, NULL)) != DS_OK) {
+ console_fprintf(stderr,"SoundStream: Cannot get the primary DS buffer address!\n");
+ IDirectSound_Release(t_pDirectSound);
+ goto out;
+ }
+
+ /* Set the primary DS buffer sound format. We have to do this because
+ the default primary buffer is 8-bit, 22kHz! */
+ if ((result = IDirectSoundBuffer_SetFormat(t_pDSPrimeBuffer, &wfFormat)) != DS_OK) {
+ console_fprintf(stderr,"SoundStream: Cannot set the primary DS buffer to proper sound format (%x) (%d)!\n", result, result);
+ IDirectSoundBuffer_Stop(t_pDSPrimeBuffer);
+ IDirectSoundBuffer_Release(t_pDSPrimeBuffer);
+ IDirectSound_Release(t_pDirectSound);
+ goto out;
+ }
+# endif /* 0 */
+
+ /* Setup the secondary DS buffer description */
+ t_cbBufSize = RT_BUFFER_SIZE * sizeof(short) * NBUFS;
+ __INST(bufferSize) = __MKSMALLINT(t_cbBufSize);
+
+ ZeroMemory(&dsbdDesc, sizeof(DSBUFFERDESC));
+ dsbdDesc.dwSize = sizeof(DSBUFFERDESC);
+ dsbdDesc.dwFlags = DSBCAPS_GLOBALFOCUS;
+ dsbdDesc.dwBufferBytes = t_cbBufSize;
+ dsbdDesc.lpwfxFormat = &wfFormat;
+
+ /* Create the secondary DS buffer */
+ if ((result = IDirectSound_CreateSoundBuffer(t_pDirectSound, &dsbdDesc, &t_pDSBuffer, NULL)) != DS_OK) {
+ console_fprintf(stderr,"SoundStream: couldn't create sound buffer!\n");
+ IDirectSoundBuffer_Stop(t_pDSPrimeBuffer);
+ IDirectSoundBuffer_Release(t_pDSPrimeBuffer);
+ IDirectSound_Release(t_pDirectSound);
+ goto out;
+ }
+
+ /* Lock the DS buffer */
+ if ((result = IDirectSoundBuffer_Lock(t_pDSBuffer, 0, t_cbBufSize, (LPLPVOID)&pDSBuffData,
+ &dwDataLen, NULL, NULL, 0)) != DS_OK) {
+ console_fprintf(stderr,"SoundStream: couldn't lock sound buffer!\n");
+ goto errorAndOut;
+ }
+
+ /* Zero the DS buffer */
+ ZeroMemory(pDSBuffData, dwDataLen);
+
+ /* Unlock the DS buffer */
+ if ((result = IDirectSoundBuffer_Unlock(t_pDSBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) {
+ console_fprintf(stderr,"SoundStream: couldn't unlock sound buffer!\n");
+ goto errorAndOut;
+ }
+
+ __INST(bufferOffset) = __MKSMALLINT(0); // reset last write position to start of buffer
+
+ /* Start the buffer playback */
+ if ((result = IDirectSoundBuffer_Play(t_pDSBuffer, 0, 0, DSBPLAY_LOOPING) != DS_OK)) {
+ console_fprintf(stderr,"SoundStream: couldn't play sound buffer!\n");
+
+errorAndOut:
+ IDirectSoundBuffer_Stop(t_pDSBuffer);
+ IDirectSoundBuffer_Stop(t_pDSPrimeBuffer);
+ IDirectSoundBuffer_Release(t_pDSPrimeBuffer);
+ IDirectSound_Release(t_pDirectSound);
+ goto out;
+ }
+
+ {
+ OBJ t;
+
+ t = __MKEXTERNALADDRESS(t_pDSBuffer); __INST(pDSBuffer) = t; __STORE(self, t);
+ t = __MKEXTERNALADDRESS(t_pDirectSound); __INST(pDirectSound) = t; __STORE(self, t);
+ }
+ ok = true;
+out:;
+#endif /* SUPPORT_WIN32_DIRECTSOUND */
+
+%}.
+ ok == false ifTrue:[
+ lastErrorString := errorStringOrNil.
+ lastErrorNumber := error ? -1.
+ self openError:error.
+ "normally not reached"
+ ^ nil.
+ ].
+ self registerForFinalization.
+! !
+
+!SoundStream::Win32DirectSound methodsFor:'queries'!
+
+supportedAudioFormats
+ "return a collection of supported audio formats.
+ possibly returned symbols are:
+ U8 unsigned 8bit samples
+ S8 signed 8bit samples
+ U16 unsigned 16bit samples in native format
+ U16_LE unsigned 16bit big endian samples
+ U16_BE unsigned 16bit big endian samples
+ S16 signed 16bit little endian samples in native format
+ S16_LE signed 16bit little endian samples
+ S16_BE signed 16bit big endian samples
+ S24 signed 24bit little endian samples in native format
+ S24_LE signed 24bit little endian samples
+ S24_BE signed 24bit big endian samples
+ S32 signed 32bit little endian samples in native format
+ S32_LE signed 32bit little endian samples
+ S32_BE signed 32bit big endian samples
+ F32 float samples
+ MPEG audio mpeg encoded
+ MU_LAW u-law encoded 8bit samples
+ A_LAW a-law encoded 8bit samples
+ IMA_ADPCM adpcm encoded
+ the set of returned symbols depends on the underlying sound hardware.
+ "
+
+ ^ #(S16_LE S16)
+
+ "
+ |s formats|
+
+ s := self writing.
+ formats := s supportedAudioFormats.
+ s close.
+ formats
+ "
+! !
+
+!SoundStream::Win32DirectSound methodsFor:'reading'!
+
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with ByteArrays only."
+
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::Win32DirectSound methodsFor:'writing'!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Redefined, since IRIS audio library cannot be used with stdio.
+ (at least I don't know). Use with ByteArrays only."
+
+%{
+#ifdef SUPPORT_WIN32_DIRECTSOUND
+ {
+ HRESULT hr;
+ DWORD status;
+ LPVOID lpbuf1 = NULL;
+ LPVOID lpbuf2 = NULL;
+ DWORD dwsize1 = 0;
+ DWORD dwsize2 = 0;
+ DWORD playPos, safePos, endWrite;
+ DWORD millis;
+ OBJ oDirectSound, oDSBuffer;
+ LPDIRECTSOUND t_pDirectSound = (LPDIRECTSOUND)0;
+ LPDIRECTSOUNDBUFFER t_pDSBuffer = (LPDIRECTSOUNDBUFFER)0;
+ int t_cbBufOffset, t_cbBufSize;
+ short *buf;
+ int cnt, offs;
+
+ if ((oDSBuffer = __INST(pDSBuffer)) != nil) {
+ t_pDSBuffer = __DSBufferVal(oDSBuffer);
+ }
+ if ((oDirectSound = __INST(pDirectSound)) != nil) {
+ t_pDirectSound = __DirectSoundVal(oDirectSound);
+ }
+
+ if (!t_pDSBuffer || !t_pDirectSound) {
+ console_fprintf(stderr, "SoundStream not open!\n");
+ RETURN (0);
+ }
+ t_cbBufOffset = __intVal(__INST(bufferOffset));
+ t_cbBufSize = __intVal(__INST(bufferSize));
+
+ cnt = __intVal(count);
+ offs = __intVal(start) - 1;
+ buf = (short *)__InstPtr(anObject) + OHDR_SIZE + offs;
+
+ // Should be playing, right?
+ hr = IDirectSoundBuffer_GetStatus(t_pDSBuffer, &status );
+ if (!(status && DSBSTATUS_PLAYING)) {
+ console_fprintf(stderr, "Buffer not playing!\n");
+ RETURN (0);
+ }
+
+ // Sleep until we have enough room in buffer.
+ hr = IDirectSoundBuffer_GetCurrentPosition(t_pDSBuffer, &playPos, &safePos );
+ if( hr != DS_OK ) {
+ console_fprintf(stderr, "Cannot get position!\n");
+ RETURN (0);
+ }
+ if( playPos < t_cbBufOffset ) playPos += t_cbBufSize;
+
+ endWrite = t_cbBufOffset + (cnt * sizeof(short));
+ while ( playPos < endWrite ) {
+ // Calculate number of milliseconds until we will have room, as
+ // time = distance * (milliseconds/second) / ((bytes/sample) * (samples/second)),
+ // rounded up.
+ millis = (DWORD) (1.0 + ((endWrite - playPos) * 1000.0) / ( sizeof(short) * __intVal(__INST(sampleRate))));
+
+ // Sleep for that long
+ Sleep( millis );
+
+ // Wake up, find out where we are now
+ hr = IDirectSoundBuffer_GetCurrentPosition(t_pDSBuffer, &playPos, &safePos );
+ if( hr != DS_OK ) {
+ console_fprintf(stderr, "Cannot get position!\n");
+ RETURN (0);
+ }
+ if( playPos < t_cbBufOffset ) playPos += t_cbBufSize; // unwrap offset
+ }
+
+ // Lock free space in the DS
+ hr = IDirectSoundBuffer_Lock(t_pDSBuffer, t_cbBufOffset, cnt * sizeof(short), &lpbuf1, &dwsize1, &lpbuf2, &dwsize2, 0);
+ if (hr == DS_OK) {
+ // Copy the buffer into the DS
+ CopyMemory(lpbuf1, buf, dwsize1);
+ if(NULL != lpbuf2) CopyMemory(lpbuf2, buf+dwsize1, dwsize2);
+
+ // Update our buffer offset and unlock sound buffer
+ t_cbBufOffset = (t_cbBufOffset + dwsize1 + dwsize2) % t_cbBufSize;
+ IDirectSoundBuffer_Unlock(t_pDSBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
+ }
+ __INST(buffferOffset) = __MKSMALLINT(t_cbBufOffset);
+
+ RETURN (count);
+ }
+#endif /* SUPPORT_WIN32_DIRECTSOUND */
+
+%}.
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::Win32WaveSound class methodsFor:'documentation'!
+
+documentation
+"
+ documentation to be added.
+
+ class:
+ <a short class summary here, describing what instances represent>
+
+ responsibilities:
+ <describing what my main role is>
+
+ collaborators:
+ <describing with whom and how I talk to>
+
+ API:
+ <public api and main messages>
+
+ example:
+ <a one-line examples on how to use - can also be in a separate example method>
+
+ implementation:
+ <implementation points>
+
+ [author:]
+ exept MBP
+
+ [instance variables:]
+
+ [class variables:]
+
+ [see also:]
+
+"
+! !
+
+!SoundStream::Win32WaveSound class methodsFor:'queries'!
+
+isSupported
+%{
+#ifdef SUPPORT_WIN32_WAVESOUND
+ RETURN(true);
+#endif
+%}.
+ ^ false.
+! !
+
+!SoundStream::Win32WaveSound methodsFor:'misc'!
+
+flush
+ "wait until all sound has been played"
+
+ "/ as yet unimplemented
+ ^ self
+! !
+
+!SoundStream::Win32WaveSound methodsFor:'open & close'!
+
+closeFile
+ "a stream has been collected - close the file"
+
+%{
+#ifdef SUPPORT_WIN32_WAVESOUND
+ struct buf *bp, *next;
+ int r;
+ HWAVEOUT t_waveHandle;
+ OBJ oWaveHandle;
+
+ if ((oWaveHandle = __INST(waveHandle)) != nil) {
+ t_waveHandle = __WaveHandleVal(oWaveHandle);
+
+# ifdef NO_WAIT_IN_CLOSE
+ /* Force cancellation of any pending buffers */
+ (void)waveOutReset(t_waveHandle);
+# endif
+
+ /* Wait until all pending buffers have been freed */
+ while (free_buffers < total_buffers) {
+ WaitForSingleObject(free_buffer_event, INFINITE);
+ }
+
+# ifndef NO_WAIT_IN_CLOSE
+ /* Force cancellation of any pending buffers */
+ (void)waveOutReset(t_waveHandle);
+# endif
+
+ /* Close the device */
+ if ((r = waveOutClose(t_waveHandle)) != 0) {
+ console_printf("waveOutClose\n");
+ RETURN(self);
+ }
+
+ EnterCriticalSection(&free_list_lock);
+
+ /* Free allocated buffers */
+ for (bp = free_list; bp != NULL; bp = next) {
+ next = bp->next;
+ (void)free(bp);
+ }
+ free_list = NULL;
+
+ LeaveCriticalSection(&free_list_lock);
+
+ __INST(waveHandle) = nil;
+ }
+ RETURN (self);
+#endif /* SUPPORT_WIN32_WAVESOUND */
+%}.
+
+!
+
+openWithMode:aMode attributes:attributeSpec
+ |ok error errorStringOrNil|
+
+%{
+#ifdef SUPPORT_WIN32_WAVESOUND
+ PCMWAVEFORMAT waveFormat;
+ int r;
+ HWAVEOUT t_waveHandle;
+ OBJ oWaveHandle;
+
+ ok = false;
+
+ if ((oWaveHandle = __INST(waveHandle)) != nil) {
+ goto out;
+ }
+
+ waveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
+ waveFormat.wf.nChannels = __intVal(__INST(numberOfChannels));
+ waveFormat.wf.nSamplesPerSec = __intVal(__INST(sampleRate));
+ waveFormat.wBitsPerSample = __intVal(__INST(bitsPerSample));
+ waveFormat.wf.nBlockAlign = waveFormat.wf.nChannels * waveFormat.wBitsPerSample / 8;
+ waveFormat.wf.nAvgBytesPerSec = waveFormat.wf.nSamplesPerSec * waveFormat.wf.nBlockAlign;
+
+ r = waveOutOpen(&t_waveHandle,
+ WAVE_MAPPER,
+ (WAVEFORMAT *)&waveFormat,
+ (DWORD_PTR)waveCallBack,
+ (DWORD_PTR)0,
+ CALLBACK_FUNCTION);
+ if (r != 0) {
+ console_printf("waveOutOpen\n");
+ goto out;
+ }
+
+ (void)waveOutReset(t_waveHandle);
+
+ free_list = NULL;
+ InitializeCriticalSection(&free_list_lock);
+ free_buffers = 0;
+ free_buffer_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ total_buffers = 0;
+
+ {
+ OBJ t;
+
+ t = __MKEXTERNALADDRESS(t_waveHandle); __INST(waveHandle) = t; __STORE(self, t);
+ }
+
+# if 0
+ /*
+ * HACK: If we immediately start writing valid audio data to the device
+ * then the sound is choppy in the beginning. Writing a null packet to
+ * to the device first seems to fix this problem although I have no idea
+ * why - DAC
+ */
+ {
+ char null[DATALEN];
+ int i;
+ for (i = 0; i < DATALEN; i++) {
+ null[i] = 127;
+ }
+ audioWrite(null, DATALEN);
+ }
+# endif /* 0 */
+ ok = true;
+
+out:;
+
+#endif /* !SUPPORT_WIN32_DIRECTSOUND */
+%}.
+ ok == false ifTrue:[
+ lastErrorString := errorStringOrNil.
+ lastErrorNumber := error ? -1.
+ self openError:error.
+ "normally not reached"
+ ^ nil.
+ ].
+ self registerForFinalization.
+! !
+
+!SoundStream::Win32WaveSound methodsFor:'queries'!
+
+supportedAudioFormats
+ "return a collection of supported audio formats.
+ possibly returned symbols are:
+ U8 unsigned 8bit samples
+ S8 signed 8bit samples
+ U16 unsigned 16bit samples in native format
+ U16_LE unsigned 16bit big endian samples
+ U16_BE unsigned 16bit big endian samples
+ S16 signed 16bit little endian samples in native format
+ S16_LE signed 16bit little endian samples
+ S16_BE signed 16bit big endian samples
+ S24 signed 24bit little endian samples in native format
+ S24_LE signed 24bit little endian samples
+ S24_BE signed 24bit big endian samples
+ S32 signed 32bit little endian samples in native format
+ S32_LE signed 32bit little endian samples
+ S32_BE signed 32bit big endian samples
+ F32 float samples
+ MPEG audio mpeg encoded
+ MU_LAW u-law encoded 8bit samples
+ A_LAW a-law encoded 8bit samples
+ IMA_ADPCM adpcm encoded
+ the set of returned symbols depends on the underlying sound hardware.
+ "
+
+ ^ #(S16_LE S16)
+
+ "
+ |s formats|
+
+ s := self writing.
+ formats := s supportedAudioFormats.
+ s close.
+ formats
+ "
+! !
+
+!SoundStream::Win32WaveSound methodsFor:'reading'!
+
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or nil on error.
+ Use with ByteArrays only."
+
+ self errorUnsupportedOperation
+! !
+
+!SoundStream::Win32WaveSound methodsFor:'writing'!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written or nil on error.
+ Redefined, since IRIS audio library cannot be used with stdio.
+ (at least I don't know). Use with ByteArrays only."
+
+%{
+#ifdef SUPPORT_WIN32_WAVESOUND
+ {
+ struct buf *bp;
+ int len, i, r;
+ int dataLen, offs;
+ short *buf;
+
+ HWAVEOUT t_waveHandle;
+ OBJ oWaveHandle;
+
+ if ((oWaveHandle = __INST(waveHandle)) == nil) {
+ RETURN(0);
+ }
+ t_waveHandle = __WaveHandleVal(oWaveHandle);
+
+ dataLen = __intVal(count);
+ offs = __intVal(start) - 1;
+ buf = (short *)__InstPtr(anObject) + OHDR_SIZE + offs;
+
+ while (dataLen > 0) {
+ if (free_list == NULL && total_buffers < MAXBUF) {
+ /* Expand available buffer space */
+ bp = (struct buf *)malloc(sizeof(struct buf));
+ total_buffers++;
+ } else {
+ if (free_list == NULL) {
+ /* We must wait for a free buffer */
+ while (free_list == NULL) {
+ WaitForSingleObject(free_buffer_event, INFINITE);
+ }
+ }
+ EnterCriticalSection(&free_list_lock);
+ bp = free_list;
+ free_list = free_list->next;
+ --free_buffers;
+ LeaveCriticalSection(&free_list_lock);
+ r = waveOutUnprepareHeader(t_waveHandle, &bp->hdr, sizeof(WAVEHDR));
+ if (r != 0) {
+ console_printf("waveOutUnprepareHeader\n");
+ RETURN(self);
+ }
+ }
+ len = min(dataLen, DATALEN);
+ bp->hdr.lpData = (char *)bp->data;
+ bp->hdr.dwBufferLength = len;
+ bp->hdr.dwBytesRecorded = len;
+ bp->hdr.dwUser = (INT)(bp);
+ bp->hdr.dwFlags = 0;
+ bp->hdr.dwLoops = 0;
+ r = waveOutPrepareHeader(t_waveHandle, &bp->hdr, sizeof(WAVEHDR));
+ if (r != 0) {
+ console_printf("waveOutPrepareHeader\n");
+ RETURN(self);
+ }
+ for (i = 0; i < len; i++) {
+ bp->data[i] = buf[i];
+ }
+ r = waveOutWrite(t_waveHandle, &bp->hdr, sizeof(WAVEHDR));
+ if (r != 0) {
+ console_printf("waveOutWrite\n");
+ RETURN(self);
+ }
+ buf += len;
+ dataLen -= len;
+ }
+ RETURN (count);
+ }
+#endif /* SUPPORT_WIN32_WAVESOUND */
+%}.
+ self errorUnsupportedOperation
+! !
+
!SoundStream class methodsFor:'documentation'!
version