Linux Audio

47 views
Skip to first unread message

Louis LaBrunda

unread,
Jul 5, 2024, 10:59:04 AMJul 5
to VAST Community Forum
Hi All,

Windows has PlatformFunctions::WaveOutSetVolume and PlatformFunctions::PlaySound that play a .wav file.  Does Linux (on the Raspberry Pi) have anything similar?

Lou

Louis LaBrunda

unread,
Jul 6, 2024, 5:31:12 PMJul 6
to VAST Community Forum
Hi,

I asked ChatGPT for help and it responded with some code that might work with an old version of VA Smalltalk.  That got me started redoing it for the new pragma style of generating PlatformFunctions.  I have done some of this before but usually get stuck defining the parms that go to/from the OS.

I created RaspberryALSAApp with this pragma:

_PRAGMA_RaspberryALSAFunctions

"%%PRAGMA DECLARE
(name: RaspberryALSAFunctions isPool: true)
(pool: RaspberryALSAFunctions declarations: (
(name: SpaOpen isConstant: true valueExpression: 'PlatformFunction fromArray: #(''C'' ''spaOpenOnDevice'' nil ''RaspberryALSA'' #(#pointer #pointer) #bool)')
(name: SpaClose isConstant: true valueExpression: 'PlatformFunction fromArray: #(''C'' ''spaClose'' nil ''RaspberryALSA'' #(#pointer ) #void)')
(name: SpaPrepare isConstant: true valueExpression: 'PlatformFunction fromArray: #(''C'' ''spaPrepare'' nil ''RaspberryALSA'' #(#pointer ) #bool)')
(name: SpaWrite isConstant: true valueExpression: 'PlatformFunction fromArray: #(''C'' ''spaWrite'' nil ''RaspberryALSA'' #(#pointer #pointer #int32 ) #bool)')
))"

I'm wondering if the second #pointer in SpaOpen shouldn't be #int32?

I also created a class: RaspberryALSAPlayer with: poolDictionaries: 'RaspberryALSAFunctions '.

and class side method:

play: fileNameAndPath on: deviceNameStringOrNil
"Open the device named, prepare it, play the file and close."
"RaspberryALSAPlayer play: '/home/lou/WiFiClock/sounds/Bong.wav'."
| spaHandle rc deviceNameString fileStream byteArray |

spaHandle := OSPtr null.
deviceNameString := deviceNameStringOrNil ifNil: ['default'].
rc := SpaOpen callWith: deviceNameString with: spaHandle.
rc ifFalse: [^nil].

rc := SpaPrepare callWith: spaHandle.
rc ifFalse: [
SpaClose callWith: spaHandle.
^nil.
].

fileStream := CfsReadFileStream open: fileNameAndPath.
fileStream isCfsError ifTrue: [
SpaClose callWith: spaHandle.
^nil.
].
byteArray := fileStream contents asByteArray.

SpaWrite callWith: spaHandle with: byteArray with: byteArray size.
SpaClose callWith: spaHandle.

I added the following line to the abt.ini file:

RaspberryALSA=/usr/lib/aarch64-linux-gnu/spa-0.2/alsa/libspa-alsa.so

Trying to test loops creating a walkback with:

UndefinedObject(Object)>>#doesNotUnderstand: nil.

I'm too tired to play with this any more today.  Any and all ideas are welcome.

Lou

Louis LaBrunda

unread,
Jul 7, 2024, 4:51:26 PMJul 7
to VAST Community Forum
Hi,

Before going into the endless loop of  UndefinedObject(Object)>>#doesNotUnderstand: nil, calling the platform function fails.  Primitive failed calling 'RaspberryALSA':spaOpenOnDevice from PlatformFunction>>#callWith:with: due to OS error1.  The walkback log is attached.

For one test I changed ' RaspberryALSA' to ' RaspberryALSAXX' and got the same error.  That seems to imply the .so isn't being found.  The .ini file contains:

RaspberryALSA=/usr/lib/aarch64-linux-gnu/spa-0.2/alsa/libspa-alsa.so

which I sure seems like the path to the lib.  

OS Error1 means (I think) permission deigned but it seems it can also mean the lib wasn't found.

I tried a bunch of things to see if the problem was in the abt.ini file.  I commented out all the RaspberryGpio... libs that control the GPIO pins, in all the .ini files I could find.  The thought was that if I run the program in test mode in the dev env it should get the same error because the libs wouldn't be found.  Well, I don't know how but the program works just fine.  How it finds the libs (.so files) I don't know?  Maybe they are defaulted somewhere?

; RaspberryGpio=libpigpio.so
; RaspberryGpioDaemon=libpigpiod_if2.so
; RaspberryGpioUltrasonicDaemon=libpigpioultrasonic.so
; RaspberryALSA=/usr/lib/aarch64-linux-gnu/spa-0.2/alsa/libspa-alsa.so

I also added this to the ini file:

RaspberryALSA=/usr/lib/aarch64-linux-gnu/libasound.so.2

to see if I was using the wrong lib.  No joy.

Now to look more into OS Error1.  But everything runs as root so...

Lou
ALSAwalkback.log

Richard Sargent

unread,
Jul 8, 2024, 3:25:09 PMJul 8
to VAST Community Forum
On Sunday, July 7, 2024 at 1:51:26 PM UTC-7 Louis LaBrunda wrote:
Hi,

Before going into the endless loop of  UndefinedObject(Object)>>#doesNotUnderstand: nil, calling the platform function fails.  Primitive failed calling 'RaspberryALSA':spaOpenOnDevice from PlatformFunction>>#callWith:with: due to OS error1.  The walkback log is attached.

For one test I changed ' RaspberryALSA' to ' RaspberryALSAXX' and got the same error.  That seems to imply the .so isn't being found.  The .ini file contains:

That does seem likely. I have some tools I use in Windows to help figure out these kinds of problems. You may be able to adapt the implementation to Linux.
One obvious question is whether you are using a 64-bit VM/image. I suspect you are, but that has bitten my butt more than a few times!
With Windows, I use a program called depend.exe (or something like that) to review the module's dependencies on other DLLs. I have no idea whether a similar tool exists for Linux.

 | module fileName |

fileName := GbsConfiguration current libraryName. "fileName can be fully qualified"
"get DLL handle to see if it is already loaded and (try to) load it if not."
(module := OSHmodule getModuleHandle: fileName) isNull
" ifTrue: [module := OSHmodule loadLibrary: fileName]"
ifTrue: [module := OSHmodule loadLibraryEx: fileName hFile: nil dwFlags: 16r00000100 "LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR" + 16r00001000 "LOAD_LIBRARY_SEARCH_DEFAULT_DIRS"]
ifFalse: [^'the dll was already loaded; nothing changed'].
module isNull
ifTrue: [self error: 'could not load the dll, error code ', PlatformGlobals::OS getLastError printString]
ifFalse: ["free the DLL"
module freeLibrary
ifTrue: ['success']
ifFalse: [self error: 'could not unload the dll']]

Louis LaBrunda

unread,
Jul 8, 2024, 4:22:42 PMJul 8
to VAST Community Forum
Hey Richard,

Thanks for looking at this.  I tried putting "libspa-alsa.so" right in the platform function, with no change in the results.  I'm now thinking this may be a permission thing.  I added my "lou" and "root" user ids toe the audio group, also no joy.  I then tried using libasound.so.2.  It is at a lower level (which means more work) and the function names are different and there are more of them.  See below.

The open function seems to work.  It answers true but I don't get the handle.  That could be me not giving it the right place to put the handle.  Below I use OSPtr and also have tried ByteArray with no luck.  In Windows I have used ByteArray and OSHandle but OSHandle is windows and not Linux.

| opf cpf rawHandle spaHandle |
opf := PlatformFunction fromArray: #('C' 'snd_pcm_open' nil 'libasound.so.2' #(#pointer #pointer) #bool).
cpf := PlatformFunction fromArray: #('C' 'snd_pcm_close' nil 'libasound.so.2' #(#pointer) #bool).
rawHandle := OSPtr new: 4.
(opf callWith: 'default' asPSZ with: rawHandle) inspect.
"(cpf callWith: rawHandle) inspect."
rawHandle inspect.

I would prefer to use libspa-alsa.so as it is simpler but I can't get it to do anything but blow up.

Lou

Richard Sargent

unread,
Jul 8, 2024, 5:01:29 PMJul 8
to va-sma...@googlegroups.com
On Mon, Jul 8, 2024 at 1:22 PM Louis LaBrunda <loulab...@gmail.com> wrote:
Hey Richard,

Thanks for looking at this.  I tried putting "libspa-alsa.so" right in the platform function, with no change in the results.  I'm now thinking this may be a permission thing.  I added my "lou" and "root" user ids toe the audio group, also no joy.  I then tried using libasound.so.2.  It is at a lower level (which means more work) and the function names are different and there are more of them.  See below.

The open function seems to work.  It answers true but I don't get the handle.  That could be me not giving it the right place to put the handle.  Below I use OSPtr and also have tried ByteArray with no luck.  In Windows I have used ByteArray and OSHandle but OSHandle is windows and not Linux.

| opf cpf rawHandle spaHandle |
opf := PlatformFunction fromArray: #('C' 'snd_pcm_open' nil 'libasound.so.2' #(#pointer #pointer) #bool).

The lower-level library arguments are more involved.
int snd_pcm_open( snd_pcm_t **handle, 
                  int card, 
                  int device, 
                  int mode );

--
You received this message because you are subscribed to a topic in the Google Groups "VAST Community Forum" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/va-smalltalk/yo2ICadfN_4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to va-smalltalk...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/va-smalltalk/113dfacc-06b4-4ae1-a78f-0e6bab89dbd6n%40googlegroups.com.

Richard Sargent

unread,
Jul 8, 2024, 5:06:08 PMJul 8
to va-sma...@googlegroups.com
On Mon, Jul 8, 2024 at 1:22 PM Louis LaBrunda <loulab...@gmail.com> wrote:
Hey Richard,

Thanks for looking at this.  I tried putting "libspa-alsa.so" right in the platform function, with no change in the results.  I'm now thinking this may be a permission thing.  I added my "lou" and "root" user ids toe the audio group, also no joy.  I then tried using libasound.so.2.  It is at a lower level (which means more work) and the function names are different and there are more of them.  See below.

It's been decades since I have done any serious *nix development. Some comments suggest using  ldd /path/to/binary to see what shared libraries are missing. Possibly, it would do the same for a .so file. Give it a try, if nothing else.


--
Reply all
Reply to author
Forward
Message has been deleted
0 new messages