On 2020/01/13 21:34, Mark Hesselink wrote:
> Dear list,
> 
> Please find attached a sndio(7) sound driver for Squeak VM 3.10.1
> (lang/squeak/vm). This driver has been fairly extensively tested by my
> family over the course of several weeks trying to implement the examples
> listed in Coding for Beginners Using Scratch by Jonathan Melmoth et al using
> Scratch 1.4.
> 
> The tarball contains the following files:
<snip>

Oh, nice! Here's a diff against the ports tree to add it all in. I had to
convert some existing patches which had been done against autoconf "output
files" to touch the input files instead.

> * GNUmakefile, bump-epoch.awk: GNU make based makefile plus helper files
> which I used while developing the driver.

btw, EPOCH is used for "version going backwards" (and can never be removed
once it's added to a port), for a normal update just REVISION is used.
I mention this because you will probably have some files in /usr/ports/plist
relating to your previous tests that you may need to remove before you can
build a package directly from ports.

Diff inline below, or at https://junkpile.org/squeak-sndio.diff in case
there are any problems with it getting mangled through mail (should be OK
though).

I don't have a machine with working audio handy right now, if you (or
someone else) could confirm that it's working as expected with this diff
then as long as the maintainer (CC'd) is happy with it then I can commit.

Index: Makefile
===================================================================
RCS file: /cvs/ports/lang/squeak/vm/Makefile,v
retrieving revision 1.28
diff -u -p -r1.28 Makefile
--- Makefile    25 Dec 2017 07:04:45 -0000      1.28
+++ Makefile    14 Jan 2020 14:59:57 -0000
@@ -6,19 +6,27 @@ SRCV =                1
 DISTFILES =    Squeak-$V-${SRCV}.src.tar.gz
 
 PKGNAME =      squeak-vm-$V.${SRCV}
-REVISION =     9
+REVISION =     10
 
 WRKDIST =      ${WRKDIR}/Squeak-$V-${SRCV}
 WRKSRC =       ${WRKDIST}/platforms/unix/config
 
+WANTLIB += GL ICE SM X11 X11-xcb Xdamage Xext Xfixes Xrender Xt
+WANTLIB += Xxf86vm c drm expat glapi m pthread sndio util xcb
+WANTLIB += xcb-dri2 xcb-dri3 xcb-glx xcb-present xcb-sync xcb-xfixes
+WANTLIB += xshmfence z
+
 SEPARATE_BUILD =       Yes
 
 MASTER_SITES = ${SQUEAK_SITE}unix-linux/
 
 RUN_DEPENDS =          squeak-sources-${SOURCEV}:lang/squeak/sources
 
-CONFIGURE_STYLE =      gnu
-AUTOCONF_VERSION =     2.60
+CONFIGURE_STYLE =      autoreconf
+AUTOCONF_VERSION =     2.69
+AUTOMAKE_VERSION =     1.16
+AUTORECONF =           ${MAKE} configure
+
 DESTDIRNAME =          ROOT
 CONFIGURE_ENV =                LDFLAGS="-lpthread ${LDFLAGS}" RANLIB=ranlib \
                        ac_cv_socklen_t=yes
@@ -26,8 +34,6 @@ USE_GMAKE =           Yes
 USE_LIBTOOL =          No
 NO_TEST =              Yes
 
-WANTLIB =              GL X11 m c SM util Xext ICE Xrender Xt pthread \
-                       xcb
 FULLV =                        $V-${SRCV}
 
 # XXX gnu-interp doesn't work with pie.
@@ -41,6 +47,9 @@ MAKE_FLAGS +=         docdir=${PREFIX}/share/do
 DESTDIRNAME =          ROOT
 SUBST_VARS +=          FULLV
 CFLAGS +=              -fgnu89-inline
+
+pre-patch:
+       @perl -pi -e s,\\r,\\n,g 
${WRKDIST}/platforms/unix/src/vm/intplugins/SoundPlugin/*.c
 
 pre-build:
        @perl -pi -e s,dprintf,debugprintf, ${WRKDIST}/platforms/unix/vm/*.c
Index: patches/patch-platforms_unix_config_Makefile
===================================================================
RCS file: patches/patch-platforms_unix_config_Makefile
diff -N patches/patch-platforms_unix_config_Makefile
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-platforms_unix_config_Makefile        14 Jan 2020 14:59:57 
-0000
@@ -0,0 +1,11 @@
+$OpenBSD$
+
+Index: platforms/unix/config/Makefile
+--- platforms/unix/config/Makefile.orig
++++ platforms/unix/config/Makefile
+@@ -1,4 +1,5 @@
+ configure : .force
++      libtoolize -c
+       ./mkacinc > acplugins.m4
+       aclocal
+       autoconf
Index: patches/patch-platforms_unix_config_acinclude_m4
===================================================================
RCS file: patches/patch-platforms_unix_config_acinclude_m4
diff -N patches/patch-platforms_unix_config_acinclude_m4
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-platforms_unix_config_acinclude_m4    14 Jan 2020 14:59:57 
-0000
@@ -0,0 +1,24 @@
+$OpenBSD$
+
+fix build on alpha
+
+Index: platforms/unix/config/acinclude.m4
+--- platforms/unix/config/acinclude.m4.orig
++++ platforms/unix/config/acinclude.m4
+@@ -255,16 +255,6 @@ fi)
+ AC_DEFINE_UNQUOTED(VM_MODULE_PREFIX,"$mkfrags_lib_prefix")
+ test "$ac_cv_module_prefix" = lib && mkfrags_lib_prefix=lib])
+ 
+-AC_DEFUN([AC_64BIT_ARCH],
+-[AC_MSG_CHECKING(for compiler flags to force 32-bit addresses)
+-case $host in
+-  alpha*)
+-    CFLAGS_32="-taso"
+-    test "$GCC" = "yes" && CC="\$(utldir)/decgcc"
+-    ;;
+-esac
+-AC_MSG_RESULT($CFLAGS_32)])
+-
+ 
+ ### plugin support
+ 
Index: patches/patch-platforms_unix_config_configure
===================================================================
RCS file: patches/patch-platforms_unix_config_configure
diff -N patches/patch-platforms_unix_config_configure
--- patches/patch-platforms_unix_config_configure       30 Dec 2013 12:14:55 
-0000      1.1
+++ /dev/null   1 Jan 1970 00:00:00 -0000
@@ -1,25 +0,0 @@
-$OpenBSD: patch-platforms_unix_config_configure,v 1.1 2013/12/30 12:14:55 
landry Exp $
-fix build on alpha
---- platforms/unix/config/configure.orig       Thu Apr 10 15:48:48 2008
-+++ platforms/unix/config/configure    Mon Dec 30 04:20:56 2013
-@@ -26202,20 +26202,6 @@ cat >>confdefs.h <<_ACEOF
- #define VM_MODULE_PREFIX "$mkfrags_lib_prefix"
- _ACEOF
- 
--test "$ac_cv_module_prefix" = lib && mkfrags_lib_prefix=lib
--{ echo "$as_me:$LINENO: checking for compiler flags to force 32-bit 
addresses" >&5
--echo $ECHO_N "checking for compiler flags to force 32-bit addresses... 
$ECHO_C" >&6; }
--case $host in
--  alpha*)
--    CFLAGS_32="-taso"
--    test "$GCC" = "yes" && CC="\$(utldir)/decgcc"
--    ;;
--esac
--{ echo "$as_me:$LINENO: result: $CFLAGS_32" >&5
--echo "${ECHO_T}$CFLAGS_32" >&6; }
--
--CFLAGS="$CFLAGS_32 $CFLAGS"
--
- { echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
- echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6; }
- if test "${ac_cv_c_bigendian+set}" = set; then
Index: patches/patch-platforms_unix_config_configure_ac
===================================================================
RCS file: patches/patch-platforms_unix_config_configure_ac
diff -N patches/patch-platforms_unix_config_configure_ac
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-platforms_unix_config_configure_ac    14 Jan 2020 14:59:57 
-0000
@@ -0,0 +1,13 @@
+$OpenBSD$
+
+Index: platforms/unix/config/configure.ac
+--- platforms/unix/config/configure.ac.orig
++++ platforms/unix/config/configure.ac
+@@ -243,7 +243,6 @@ AC_FUNC_ALLOCA
+ AC_GNU_OPT
+ AC_GNU_INTERP
+ AC_MODULE_LIB_PREFIX
+-AC_64BIT_ARCH
+ 
+ CFLAGS="$CFLAGS_32 $CFLAGS"
+ 
Index: patches/patch-platforms_unix_npsqueak_Makefile
===================================================================
RCS file: 
/cvs/ports/lang/squeak/vm/patches/patch-platforms_unix_npsqueak_Makefile,v
retrieving revision 1.1
diff -u -p -r1.1 patch-platforms_unix_npsqueak_Makefile
--- patches/patch-platforms_unix_npsqueak_Makefile      23 Jul 2008 11:17:22 
-0000      1.1
+++ patches/patch-platforms_unix_npsqueak_Makefile      14 Jan 2020 14:59:57 
-0000
@@ -1,6 +1,7 @@
 $OpenBSD: patch-platforms_unix_npsqueak_Makefile,v 1.1 2008/07/23 11:17:22 
espie Exp $
---- platforms/unix/npsqueak/Makefile.orig      Sat Oct 13 05:03:33 2007
-+++ platforms/unix/npsqueak/Makefile   Wed Jul 23 13:07:25 2008
+Index: platforms/unix/npsqueak/Makefile
+--- platforms/unix/npsqueak/Makefile.orig
++++ platforms/unix/npsqueak/Makefile
 @@ -8,7 +8,7 @@ INC    = -I./include -I/usr/X11R6/include
  CC    = gcc
  CFLAGS        = $(INC) -O2 -fPIC -Wall
@@ -10,3 +11,12 @@ $OpenBSD: patch-platforms_unix_npsqueak_
  
  # usually overridden from top level makefile
  VM_VERSION=  3.9-12
+@@ -26,7 +26,7 @@ all: npsqueak.so npsqueakrun
+ 
+ npsqueak.so : npsqueak.la 
+       -@rm -f $@ 
+-      ln .libs/npsqueak $@ 
++      ln .libs/npsqueak.so $@ 
+ 
+ npsqueak.la : npsqueak.lo npunix.lo 
+       ../libtool --mode=link $(LD) $(LDFLAGS) npsqueak.lo npunix.lo 
-avoid-version -module -rpath $(plgdir) -o $@
Index: patches/patch-platforms_unix_src_vm_intplugins_SoundPlugin_SoundPlugin_c
===================================================================
RCS file: 
patches/patch-platforms_unix_src_vm_intplugins_SoundPlugin_SoundPlugin_c
diff -N patches/patch-platforms_unix_src_vm_intplugins_SoundPlugin_SoundPlugin_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-platforms_unix_src_vm_intplugins_SoundPlugin_SoundPlugin_c    
14 Jan 2020 14:59:57 -0000
@@ -0,0 +1,87 @@
+$OpenBSD$
+
+Index: platforms/unix/src/vm/intplugins/SoundPlugin/SoundPlugin.c
+--- platforms/unix/src/vm/intplugins/SoundPlugin/SoundPlugin.c.orig
++++ platforms/unix/src/vm/intplugins/SoundPlugin/SoundPlugin.c
+@@ -211,24 +211,27 @@ EXPORT(sqInt) primitiveSoundInsertSamples(void) {
+ /*    Output a buffer's worth of sound samples. */
+ 
+ EXPORT(sqInt) primitiveSoundPlaySamples(void) {
+-      sqInt framesPlayed;
++      sqInt buffer;
+       sqInt frameCount;
+-      usqInt *buf;
++      sqInt framesPlayed;
+       sqInt startIndex;
+       sqInt _return_value;
++      char *bufferData;
+ 
+       frameCount = interpreterProxy->stackIntegerValue(2);
+-      
interpreterProxy->success(interpreterProxy->isWords(interpreterProxy->stackValue(1)));
+-      buf = ((unsigned *) 
(interpreterProxy->firstIndexableField(interpreterProxy->stackValue(1))));
++      buffer = interpreterProxy->stackValue(1);
+       startIndex = interpreterProxy->stackIntegerValue(0);
++      interpreterProxy->success(interpreterProxy->isWords(buffer));
+       if (interpreterProxy->failed()) {
+               return null;
+       }
+-      interpreterProxy->success((startIndex >= 1) && (((startIndex + 
frameCount) - 1) <= (interpreterProxy->slotSizeOf(((sqInt)(long)(buf) - 4)))));
+-      if (!(interpreterProxy->failed())) {
+-              framesPlayed = snd_PlaySamplesFromAtLength(frameCount, 
(int)buf, startIndex - 1);
+-              interpreterProxy->success(framesPlayed >= 0);
++      interpreterProxy->success((startIndex >= 1) && (((startIndex + 
frameCount) - 1) <= (interpreterProxy->slotSizeOf(buffer))));
++      if (interpreterProxy->failed()) {
++              return null;
+       }
++      bufferData = interpreterProxy->firstIndexableField(buffer);
++      framesPlayed = snd_PlaySamplesFromAtLength(frameCount, 
oopForPointer(bufferData), startIndex - 1);
++      interpreterProxy->success(framesPlayed >= 0);
+       _return_value = interpreterProxy->positive32BitIntegerFor(framesPlayed);
+       if (interpreterProxy->failed()) {
+               return null;
+@@ -261,25 +264,27 @@ EXPORT(sqInt) primitiveSoundPlaySilence(void) {
+ /*    Record a buffer's worth of 16-bit sound samples. */
+ 
+ EXPORT(sqInt) primitiveSoundRecordSamples(void) {
+-      sqInt bufSizeInBytes;
++      sqInt buffer;
++      sqInt bufferSizeInSamples;
+       sqInt samplesRecorded;
+-      usqInt *buf;
+-      sqInt startWordIndex;
++      sqInt startIndex;
+       sqInt _return_value;
++      char *bufferData;
+ 
+-      
interpreterProxy->success(interpreterProxy->isWords(interpreterProxy->stackValue(1)));
+-      buf = ((unsigned *) 
(interpreterProxy->firstIndexableField(interpreterProxy->stackValue(1))));
+-      startWordIndex = interpreterProxy->stackIntegerValue(0);
++      buffer = interpreterProxy->stackValue(1);
++      startIndex = interpreterProxy->stackIntegerValue(0);
++      interpreterProxy->success(interpreterProxy->isWords(buffer));
+       if (interpreterProxy->failed()) {
+               return null;
+       }
+-      if (!(interpreterProxy->failed())) {
+-              bufSizeInBytes = 
(interpreterProxy->slotSizeOf(((sqInt)(long)(buf) - 4))) * 4;
+-              interpreterProxy->success((startWordIndex >= 1) && 
(((startWordIndex - 1) * 2) < bufSizeInBytes));
++      bufferSizeInSamples = interpreterProxy->slotSizeOf(buffer) * 2;
++      interpreterProxy->success((startIndex >= 1) && ((startIndex - 1) <= 
bufferSizeInSamples));
++      if (interpreterProxy->failed()) {
++              return null;
+       }
+-      if (!(interpreterProxy->failed())) {
+-              samplesRecorded = snd_RecordSamplesIntoAtLength((int)buf, 
startWordIndex - 1, bufSizeInBytes);
+-      }
++      bufferData = interpreterProxy->firstIndexableField(buffer);
++      samplesRecorded = 
snd_RecordSamplesIntoAtLength(oopForPointer(bufferData), startIndex - 1, 
bufferSizeInSamples * 2);
++      interpreterProxy->success(samplesRecorded >= 0);
+       _return_value = 
interpreterProxy->positive32BitIntegerFor(samplesRecorded);
+       if (interpreterProxy->failed()) {
+               return null;
+@@ -471,4 +476,5 @@ void* SoundPlugin_exports[][3] = {
+ 
+ 
+ #endif /* ifdef SQ_BUILTIN_PLUGIN */
++
+ 
Index: patches/patch-platforms_unix_vm-sound-sndio_Makefile_inc
===================================================================
RCS file: patches/patch-platforms_unix_vm-sound-sndio_Makefile_inc
diff -N patches/patch-platforms_unix_vm-sound-sndio_Makefile_inc
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-platforms_unix_vm-sound-sndio_Makefile_inc    14 Jan 2020 
14:59:57 -0000
@@ -0,0 +1,21 @@
+$OpenBSD$
+
+Index: platforms/unix/vm-sound-sndio/Makefile.inc
+--- platforms/unix/vm-sound-sndio/Makefile.inc.orig
++++ platforms/unix/vm-sound-sndio/Makefile.inc
+@@ -0,0 +1,15 @@
++# Copyright (c) 2019 Mark Hesselink <[email protected]>
++#
++# Permission to use, copy, modify, and/or distribute this software for any
++# purpose with or without fee is hereby granted, provided that the above
++# copyright notice and this permission notice appear in all copies.
++#
++# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
++# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
++# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
++# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++PLIBS = -lsndio
Index: patches/patch-platforms_unix_vm-sound-sndio_acinclude_m4
===================================================================
RCS file: patches/patch-platforms_unix_vm-sound-sndio_acinclude_m4
diff -N patches/patch-platforms_unix_vm-sound-sndio_acinclude_m4
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-platforms_unix_vm-sound-sndio_acinclude_m4    14 Jan 2020 
14:59:57 -0000
@@ -0,0 +1,27 @@
+$OpenBSD$
+
+Index: platforms/unix/vm-sound-sndio/acinclude.m4
+--- platforms/unix/vm-sound-sndio/acinclude.m4.orig
++++ platforms/unix/vm-sound-sndio/acinclude.m4
+@@ -0,0 +1,21 @@
++dnl Copyright (c) 2019 Mark Hesselink <[email protected]>
++dnl
++dnl Permission to use, copy, modify, and/or distribute this software for any
++dnl purpose with or without fee is hereby granted, provided that the above
++dnl copyright notice and this permission notice appear in all copies.
++dnl
++dnl THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++dnl WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++dnl MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
ANY
++dnl SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++dnl WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
ACTION
++dnl OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
++dnl CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++
++AC_MSG_CHECKING([for OpenBSD sndio sound support])
++AC_TRY_COMPILE([#include <sndio.h>],[(void)sio_open;],[
++  AC_MSG_RESULT([yes])
++],[
++  AC_MSG_RESULT([no])
++  AC_PLUGIN_DISABLE
++])
Index: patches/patch-platforms_unix_vm-sound-sndio_sqUnixSoundSndio_c
===================================================================
RCS file: patches/patch-platforms_unix_vm-sound-sndio_sqUnixSoundSndio_c
diff -N patches/patch-platforms_unix_vm-sound-sndio_sqUnixSoundSndio_c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ patches/patch-platforms_unix_vm-sound-sndio_sqUnixSoundSndio_c      14 Jan 
2020 14:59:57 -0000
@@ -0,0 +1,637 @@
+$OpenBSD$
+
+Index: platforms/unix/vm-sound-sndio/sqUnixSoundSndio.c
+--- platforms/unix/vm-sound-sndio/sqUnixSoundSndio.c.orig
++++ platforms/unix/vm-sound-sndio/sqUnixSoundSndio.c
+@@ -0,0 +1,631 @@
++/* vim: set et sw=4 ts=4:
++ * Copyright (c) 2019 Mark Hesselink <[email protected]>
++ *
++ * Permission to use, copy, modify, and/or distribute this software for any
++ * purpose with or without fee is hereby granted, provided that the above
++ * copyright notice and this permission notice appear in all copies.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
++ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
ACTION
++ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
++ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
++ *
++ * Squeak VM sound module for OpenBSD sndio sound system.
++ */
++
++/* Squeak VM uses the following C++-like pseudo-code to play audio:
++ *
++ *  void PlayLoop(Semaphore &playerSemaphore, Condition &readyForData,
++ *                Buffer &buffer, int stereoFlag)
++ *  {
++ *      const int bytesPerFrame = stereoFlag ? 4 : 2;
++ *      const int startingAt = 0;
++ *      int frameCount;
++ *      int framesPlayed;
++ *
++ *      for (;;)
++ *      {
++ *          while (
++ *              (frameCount = primSoundAvailableBytes() / bytesPerFrame) < 
100)
++ *          {
++ *              readyForData.Wait();
++ *          }
++ *          framecount = MIN(framecount, buffer.StereoSampleCount());
++ *          playerSemaphore.Acquire();
++ *          buffer.Refill();
++ *          framesPlayed = primSoundPlaySamples(framecount, buffer.Data(),
++ *                                              startingAt);
++ *          playerSemaphore.Release();
++ *      }
++ *  }
++ *
++ *
++ * And the following C++-like pseudo-code to record audio:
++ *
++ *  void RecordLoop(Sound &sound, Condition &dataAvailable, Buffer &buffer,
++ *                  int stereoFlag)
++ *  {
++ *      int sampleCount;
++ *      int samplesRecorded = 0;
++ *      int startingAt = 0;
++ *
++ *      sampleCount = stereoFlag ? buffer.StereoSampleCount() :
++ *                    buffer.MonoSampleCount();
++ *      for (;;)
++ *      {
++ *          if (samplesRecorded == 0)
++ *          {
++ *              dataAvailable.Wait();
++ *          }
++ *          samplesRecorded = primRecordSamplesInto(buffer.Data(), 
startingAt);
++ *          startingAt += samplesRecorded;
++ *          if (startingAt > sampleCount)
++ *          {
++ *              sound.Add(buffer);
++ *              buffer.Reset();
++ *              startingAt = 0;
++ *          }
++ *      }
++ *  }
++ *
++ * Running of the play or record loop is preceded by a primStart() and
++ * primStartRecording() function call, respectively, to provide the sound
++ * driver the required information to configure the sound device.
++ *
++ * All Squeak objects, such as the `readyForData' and `dataAvailable' 
condition
++ * variables or the buffer data arrays, are passed to the sound driver in the
++ * form of an object-oriented pointer (OOP). This OOP must be converted into a
++ * C pointer if the sound driver is to access the object directly.
++ */
++
++#include "sq.h"
++#include "sqaio.h"
++
++#include <sys/socket.h>
++#include <assert.h>
++#include <err.h>
++#include <errno.h>
++#include <poll.h>
++#include <math.h>
++#include <sndio.h>
++#include <stdio.h>
++#include <time.h>
++
++/*****************************************************************************
++ * Constants, Macros & Types
++ 
*****************************************************************************/
++
++/* Number of playback buffer entries. Each entry can fit a single application
++ * buffer rounded up to the nearest block boundary worth of audio frames. */
++#define NUM_PBUF 8
++/* Number of recording buffer entries. Each entry can fit a single application
++ * buffer rounded up to the nearest block boundary worth of audio frames. */
++#define NUM_RBUF 8
++/* Minimum number of blocks that the sndio(7) sound device file handle must be
++ * able to accept before the sndio(7) sound driver is woken up. */
++#define NUM_SBLK 4
++
++#define SQUEAK_MAX_CHANNELS 2
++#define SQUEAK_SAMPLE_SIZE 2 /* bytes */
++#define SQUEAK_FRAME_SIZE (SQUEAK_MAX_CHANNELS * SQUEAK_SAMPLE_SIZE)
++
++#define DPRINTF(format, ...)                                                \
++    do {                                                                    \
++        if (verbose) {                                                      \
++            struct timespec t;                                              \
++            int rv = clock_gettime(CLOCK_MONOTONIC, &t);                    \
++            fprintf(stderr, "%.6f:%s:%d:%s: " format "\n",                  \
++                    rv == 0 ? ((double)t.tv_sec + (t.tv_nsec / 1e9)) : 0.0, \
++                    __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);       \
++        }                                                                   \
++    } while (0)
++
++#define MIN(a, b) (((a) < (b)) ? (a) : (b))
++
++struct sndio_play {
++    char *devname;
++    struct sio_hdl *hdl;
++    struct sio_par params;
++    int fd;
++    double volume;
++
++    char *buf;
++    size_t entrysz;
++    size_t head;
++    size_t tail;
++    size_t offset[NUM_PBUF];
++    size_t size[NUM_PBUF];
++
++    sqInt ready;
++    int pending;
++};
++
++struct sndio_rec {
++    char *devname;
++    struct sio_hdl *hdl;
++    struct sio_par params;
++    int fd;
++
++    char *buf;
++    size_t entrysz;
++    size_t head;
++    size_t hoffset;
++    size_t hsize;
++    size_t tail;
++    size_t toffset;
++    size_t tsize;
++
++    sqInt available;
++    int pending;
++};
++
++/*****************************************************************************
++ * Local Variables
++ 
*****************************************************************************/
++
++static struct sndio_play pc = {
++    .devname = SIO_DEVANY, .hdl = NULL, .buf = NULL};
++
++static struct sndio_rec rc = {.devname = SIO_DEVANY, .hdl = NULL, .buf = 
NULL};
++
++static int verbose = 0;
++
++/*****************************************************************************
++ * Local Function Prototypes
++ 
*****************************************************************************/
++
++static sqInt sound_Stop(void);
++
++static sqInt sound_StopRecording(void);
++
++/*****************************************************************************
++ * Local Functions
++ 
*****************************************************************************/
++
++static int init_aio(struct sio_hdl *hdl, aioHandler aio_cb, int flags)
++{
++    struct pollfd pfd;
++
++    if ((sio_nfds(hdl) != 1) || !sio_pollfd(hdl, &pfd, 0)) {
++        return -1;
++    }
++    /* It is assumed from this point onward that the number of file 
descriptors
++     * used by the sndio(7) audio playback handle remains fixed at 1. */
++    aioEnable(pfd.fd, 0, AIO_EXT);
++    aioHandle(pfd.fd, aio_cb, flags);
++
++    return pfd.fd;
++}
++
++static int init_handle(struct sio_hdl **hdl, const char *devname,
++                       unsigned int mode, struct sio_par *params,
++                       sqInt frameCount, sqInt samplesPerSec, sqInt 
stereoFlag)
++{
++    if ((*hdl = sio_open(devname, mode, 1)) == NULL) {
++        return 0;
++    }
++
++    sio_initpar(params);
++    /* Squeak VM always uses signed 16-bit native endian samples. */
++    params->bits = 8 * SQUEAK_SAMPLE_SIZE;
++    params->bps = stereoFlag || (mode == SIO_REC) ? SIO_BPS(params->bits)
++                                                  : SQUEAK_FRAME_SIZE;
++    params->sig = 1;
++    params->le = SIO_LE_NATIVE;
++    params->msb = 0;
++    params->rate = samplesPerSec;
++    params->appbufsz = frameCount;
++    params->pchan = params->rchan = stereoFlag ? 2 : 1;
++    params->xrun = SIO_IGNORE;
++    if (!sio_setpar(*hdl, params) || !sio_getpar(*hdl, params)) {
++        return 0;
++    }
++
++    return 1;
++}
++
++static void play_cb(int fd, void *user_data, int flags)
++{
++    struct pollfd pfd;
++    size_t nwritten = 0;
++    int nfds;
++    char *buffer;
++
++    if (pc.hdl == NULL) {
++        return;
++    }
++
++    /* Let the sndio(7) sound driver process messages sent by the sndiod(8)
++     * audio server. This is a prerequisite for the sndio(7) sound driver to
++     * operate in non-blocking mode.
++     *
++     * Note: Since the pollfd data structure is filled in based on the current
++     * messaging state between the sndio(7) sound driver and the sndiod(8)
++     * audio server, it must be filled in prior to every poll(2) system call.
++     */
++    if (!sio_pollfd(pc.hdl, &pfd, POLLOUT)) {
++        goto ABORT;
++    }
++    if ((nfds = poll(&pfd, 1, 0)) <= 0) {
++        if ((nfds == 0) || (errno == EINTR)) {
++            goto EXIT;
++        }
++        goto ABORT;
++    }
++    if ((sio_revents(pc.hdl, &pfd) & POLLOUT) == 0) {
++        /* Prevent Squeak VM from busy-waiting on asynchronous I/O as much as
++         * possible when new audio frames can be written to the sndio(7) sound
++         * driver's file descriptor, but the sndiod(8) audio server is not yet
++         * ready to receive these new audio frames. */
++        DPRINTF("sndiod(8) audio server not ready");
++        goto EXIT;
++    }
++
++    if (pc.tail != pc.head) {
++        buffer = &pc.buf[pc.tail * pc.entrysz + pc.offset[pc.tail]];
++        nwritten = sio_write(pc.hdl, buffer, pc.size[pc.tail]);
++        if ((nwritten == 0) && sio_eof(pc.hdl)) {
++            goto ABORT;
++        }
++        pc.offset[pc.tail] += nwritten;
++        pc.size[pc.tail] -= nwritten;
++        if (pc.size[pc.tail] == 0) {
++            pc.tail = (pc.tail + 1) % NUM_PBUF;
++            if (!pc.pending) {
++                /* Signal Squeak VM that the sndio(7) sound driver can accept
++                 * more audio frames. */
++                signalSemaphoreWithIndex(pc.ready);
++                pc.pending = 1;
++            }
++        }
++    }
++    DPRINTF("phead=%zu ptail=%zu[offset=%zu,size=%zu] nwritten=%zu", pc.head,
++            pc.tail, pc.offset[pc.tail], pc.size[pc.tail], nwritten);
++
++EXIT:
++    aioHandle(fd, play_cb, flags);
++    return;
++
++ABORT:
++    /* REVISIT: Should a warning to the console be issued here? */
++    ;
++}
++
++static void record_cb(int fd, void *user_data, int flags)
++{
++    struct pollfd pfd;
++    size_t nread = 0;
++    int nfds;
++    char *buffer;
++
++    if (rc.hdl == NULL) {
++        return;
++    }
++
++    /* Let the sndio(7) sound driver process messages sent by the sndiod(8)
++     * audio server. This is a prerequisite for the sndio(7) sound driver to
++     * operate in non-blocking mode.
++     *
++     * Note: Since the pollfd data structure is filled in based on the current
++     * messaging state between the sndio(7) sound driver and the sndiod(8)
++     * audio server, it must be filled in prior to every poll(2) system call.
++     */
++    if (!sio_pollfd(rc.hdl, &pfd, POLLIN)) {
++        goto ABORT;
++    }
++    if ((nfds = poll(&pfd, 1, 0)) <= 0) {
++        if ((nfds == 0) || (errno == EINTR)) {
++            goto EXIT;
++        }
++        goto ABORT;
++    }
++    if ((sio_revents(rc.hdl, &pfd) & POLLIN) == 0) {
++        /* Prevent Squeak VM from busy-waiting on asynchronous I/O as much as
++         * possible when new audio samples can be read from the sndio(7) sound
++         * driver's file descriptor, but the sndiod(8) audio server is not yet
++         * ready to send these new audio samples. */
++        DPRINTF("sndiod(8) audio server not ready");
++        goto EXIT;
++    }
++
++    if (((rc.head >= rc.tail) && (((rc.tail + NUM_RBUF) - rc.head) > 1)) ||
++        ((rc.head < rc.tail) && ((rc.tail - rc.head) > 1))) {
++        buffer = &rc.buf[rc.head * rc.entrysz + rc.hoffset];
++        nread = sio_read(rc.hdl, buffer, rc.hsize);
++        if ((nread == 0) && sio_eof(rc.hdl)) {
++            goto ABORT;
++        }
++        rc.hoffset += nread;
++        rc.hsize -= nread;
++        if (rc.hsize == 0) {
++            rc.head = (rc.head + 1) % NUM_RBUF;
++            rc.hoffset = 0;
++            rc.hsize = rc.entrysz;
++            if (!rc.pending) {
++                /* Signal Squeak VM that new audio samples are available. */
++                signalSemaphoreWithIndex(rc.available);
++                rc.pending = 1;
++            }
++        }
++    }
++    DPRINTF("rhead=%zu[offset=%zu,size=%zu] rtail=%zu", rc.head, rc.hoffset,
++            rc.hsize, rc.tail);
++
++EXIT:
++    aioHandle(fd, record_cb, flags);
++    return;
++
++ABORT:
++    /* REVISIT: Should a warning to the console be issued here? */
++    ;
++}
++
++static void volume_cb(void *user_data, unsigned int volume)
++{
++    pc.volume = ((double)volume) / ((double)SIO_MAXVOL);
++    DPRINTF("volume=%u pvolume=%.6f", volume, pc.volume);
++}
++
++static sqInt sound_AvailableSpace(void)
++{
++    sqInt space = 0;
++
++    pc.pending = 0;
++    if (((pc.head >= pc.tail) && (((pc.tail + NUM_PBUF) - pc.head) > 1)) ||
++        ((pc.head < pc.tail) && ((pc.tail - pc.head) > 1))) {
++        space = pc.entrysz;
++    }
++    DPRINTF("space=%d phead=%zu ptail=%zu", space, pc.head, pc.tail);
++    return space;
++}
++
++static double sound_GetRecordingSampleRate(void)
++{
++    /* REVISIT: How best to determine the sound device's recording
++     * capabilities? */
++    const unsigned int rrate = 44100;
++    DPRINTF("rrate=%u", rrate);
++    return (double)rrate;
++}
++
++static sqInt sound_InsertSamplesFromLeadTime(sqInt frameCount, sqInt 
dataIndex,
++                                             sqInt samplesOfLeadTime)
++{
++    DPRINTF("frameCount=%d samplesOfLeadTime=%d", frameCount,
++            samplesOfLeadTime);
++    return frameCount;
++}
++
++static sqInt sound_PlaySamplesFromAtLength(sqInt frameCount, sqInt dataIndex,
++                                           sqInt startingAt)
++{
++    char *data;
++
++    data = pointerForOop(dataIndex) + startingAt * SQUEAK_FRAME_SIZE;
++    pc.offset[pc.head] = 0;
++    pc.size[pc.head] = frameCount * pc.params.bps * pc.params.pchan;
++    (void)memcpy(&pc.buf[pc.head * pc.entrysz], data, pc.size[pc.head]);
++    pc.head = (pc.head + 1) % NUM_PBUF;
++    DPRINTF("frameCount=%d startingAt=%d phead=%zu ptail=%zu", frameCount,
++            startingAt, pc.head, pc.tail);
++    return frameCount;
++}
++
++static sqInt sound_PlaySilence(void) { return 0; }
++
++static sqInt sound_RecordSamplesIntoAtLength(sqInt dataIndex, sqInt 
startingAt,
++                                             sqInt bufferSizeInBytes)
++{
++    sqInt sampleCount = 0;
++    size_t nbytes;
++    char *buffer;
++    char *data;
++
++    DPRINTF("startingAt=%d bufferSizeInBytes=%d", startingAt,
++            bufferSizeInBytes);
++    rc.pending = 0;
++    if (rc.tail != rc.head) {
++        data = pointerForOop(dataIndex) + startingAt * SQUEAK_SAMPLE_SIZE;
++        buffer = &rc.buf[rc.tail * rc.entrysz + rc.toffset];
++        nbytes =
++            MIN(bufferSizeInBytes - startingAt * SQUEAK_SAMPLE_SIZE, 
rc.tsize);
++        assert((nbytes % SQUEAK_SAMPLE_SIZE) == 0);
++        sampleCount = nbytes / SQUEAK_SAMPLE_SIZE;
++        (void)memcpy(data, buffer, nbytes);
++        rc.toffset += nbytes;
++        rc.tsize -= nbytes;
++        if (rc.tsize == 0) {
++            rc.tail = (rc.tail + 1) % NUM_RBUF;
++            rc.toffset = 0;
++            rc.tsize = rc.entrysz;
++        }
++    }
++    return sampleCount;
++}
++
++static void sound_SetVolume(double left, double right)
++{
++    unsigned int volume;
++
++    DPRINTF("left=%.6f right=%.6f", left, right);
++    volume = (unsigned int)round(MIN(left, right) * SIO_MAXVOL);
++    /* REVISIT: How to inform the user that the requested volume change did 
not
++     * take effect? */
++    (void)sio_setvol(pc.hdl, volume);
++}
++
++static sqInt sound_SetRecordLevel(sqInt level)
++{
++    DPRINTF("level=%d", level);
++    return 1;
++}
++
++static sqInt sound_Start(sqInt frameCount, sqInt samplesPerSec,
++                         sqInt stereoFlag, sqInt readyForDataIndex)
++{
++    socklen_t watlen = sizeof(int);
++    int watval;
++
++    DPRINTF("frameCount=%d samplesPerSec=%d stereoFlag=%d", frameCount,
++            samplesPerSec, stereoFlag);
++
++    if (pc.hdl != NULL) {
++        /* It is safe to ignore the sound_Stop() return value as the function
++         * can't fail. */
++        (void)sound_Stop();
++    }
++
++    if (!init_handle(&pc.hdl, pc.devname, SIO_PLAY, &pc.params, frameCount,
++                     samplesPerSec, stereoFlag)) {
++        goto ABORT;
++    }
++
++    pc.entrysz = pc.params.appbufsz + pc.params.round - 1;
++    pc.entrysz -= pc.entrysz % pc.params.round;
++    pc.entrysz *= pc.params.bps * pc.params.pchan;
++    if ((pc.buf = reallocarray(pc.buf, NUM_PBUF, pc.entrysz)) == NULL) {
++        goto ABORT;
++    }
++    pc.head = pc.tail = 0;
++    pc.ready = readyForDataIndex;
++
++    if (!sio_onvol(pc.hdl, volume_cb, NULL)) {
++        goto ABORT;
++    }
++    if (!sio_start(pc.hdl)) {
++        goto ABORT;
++    }
++
++    if ((pc.fd = init_aio(pc.hdl, play_cb, AIO_W)) == -1) {
++        goto ABORT;
++    }
++
++    /* REVISIT: Is it safe to assume that the sndio(7) sound driver always
++     * communicates with the sound device via the sndiod(8) audio server? */
++    watval = NUM_SBLK * pc.params.round * pc.params.bps * pc.params.pchan;
++    if (setsockopt(pc.fd, SOL_SOCKET, SO_SNDLOWAT, &watval, watlen) != 0) {
++        goto ABORT;
++    }
++
++    return 1;
++
++ABORT:
++    (void)sound_Stop();
++    return 0;
++}
++
++static sqInt sound_StartRecording(sqInt samplesPerSec, sqInt stereoFlag,
++                                  sqInt dataAvailableIndex)
++{
++    const sqInt frameCount = samplesPerSec / 4;
++
++    DPRINTF("samplesPerSec=%d stereoFlag=%d", samplesPerSec, stereoFlag);
++
++    if (rc.hdl != NULL) {
++        /* It is safe to ignore the sound_StopRecording() return value as the
++         * function can't fail. */
++        (void)sound_StopRecording();
++    }
++
++    if (!init_handle(&rc.hdl, rc.devname, SIO_REC, &rc.params, frameCount,
++                     samplesPerSec, stereoFlag)) {
++        goto ABORT;
++    }
++
++    rc.available = dataAvailableIndex;
++    rc.entrysz = rc.params.appbufsz + rc.params.round - 1;
++    rc.entrysz -= rc.entrysz % rc.params.round;
++    rc.entrysz *= rc.params.bps * rc.params.pchan;
++    if ((rc.buf = reallocarray(rc.buf, NUM_RBUF, rc.entrysz)) == NULL) {
++        goto ABORT;
++    }
++    rc.head = rc.tail = 0;
++    rc.hoffset = rc.toffset = 0;
++    rc.hsize = rc.tsize = rc.entrysz;
++
++    if (!sio_start(rc.hdl)) {
++        goto ABORT;
++    }
++
++    if ((rc.fd = init_aio(rc.hdl, record_cb, AIO_R)) == -1) {
++        goto ABORT;
++    }
++
++    return 1;
++
++ABORT:
++    (void)sound_StopRecording();
++    return 0;
++}
++
++static sqInt sound_Stop(void)
++{
++    DPRINTF("stopping playback");
++    if (pc.hdl != NULL) {
++        aioDisable(pc.fd);
++        sio_close(pc.hdl);
++        pc.hdl = NULL;
++        free(pc.buf);
++        pc.buf = NULL;
++    }
++    return 1;
++}
++
++static sqInt sound_StopRecording(void)
++{
++    DPRINTF("stopping recording");
++    if (rc.hdl != NULL) {
++        aioDisable(rc.fd);
++        sio_close(rc.hdl);
++        rc.hdl = NULL;
++        free(rc.buf);
++        rc.buf = NULL;
++    }
++    return 1;
++}
++
++static void sound_Volume(double *left, double *right)
++{
++    *left = *right = pc.volume;
++    DPRINTF("left=%.6f right=%.6f", *left, *right);
++}
++
++#include "SqSound.h"
++
++SqSoundDefine(sndio);
++
++#include "SqModule.h"
++
++static void *sound_makeInterface(void) { return &sound_sndio_itf; }
++
++static int sound_parseArgument(int argc, char **argv) { return 0; }
++
++static void sound_parseEnvironment(void)
++{
++    const char *errstr;
++    char *strval;
++
++    if ((strval = getenv("SQUEAK_SNDIO_INPUT")) != NULL) {
++        rc.devname = strval;
++    }
++    if ((strval = getenv("SQUEAK_SNDIO_OUTPUT")) != NULL) {
++        pc.devname = strval;
++    }
++    if ((strval = getenv("SQUEAK_SNDIO_LOG_LEVEL")) != NULL) {
++        verbose = strtonum(strval, 0, 1, &errstr) != 0;
++        if (errstr != NULL) {
++            warnx("log level is %s: %s", errstr, strval);
++        }
++    }
++    DPRINTF("pdevname=%s rdevname=%s verbose=%d", pc.devname, rc.devname,
++            verbose);
++}
++
++static void sound_printUsage(void) {}
++
++static void sound_printUsageNotes(void) {}
++
++SqModuleDefine(sound, sndio);
Index: patches/patch-platforms_unix_vm_sqUnixMain_c
===================================================================
RCS file: 
/cvs/ports/lang/squeak/vm/patches/patch-platforms_unix_vm_sqUnixMain_c,v
retrieving revision 1.6
diff -u -p -r1.6 patch-platforms_unix_vm_sqUnixMain_c
--- patches/patch-platforms_unix_vm_sqUnixMain_c        6 May 2017 17:15:20 
-0000       1.6
+++ patches/patch-platforms_unix_vm_sqUnixMain_c        14 Jan 2020 14:59:57 
-0000
@@ -1,4 +1,5 @@
-$OpenBSD: patch-platforms_unix_vm_sqUnixMain_c,v 1.6 2017/05/06 17:15:20 espie 
Exp $
+$OpenBSD$
+
 Index: platforms/unix/vm/sqUnixMain.c
 --- platforms/unix/vm/sqUnixMain.c.orig
 +++ platforms/unix/vm/sqUnixMain.c
@@ -16,7 +17,15 @@ Index: platforms/unix/vm/sqUnixMain.c
  #undef        DEBUG_MODULES
  
  #undef        IMAGE_DUMP                              /* define to enable 
SIGHUP and SIGQUIT handling */
-@@ -1329,17 +1325,6 @@ int main(int argc, char **argv, char **envp)
+@@ -698,6 +694,7 @@ static struct moduleDescription moduleDescriptions[]=
+   { &displayModule, "display", "custom" },    /*** NO DEFAULT ***/
+   { &soundModule,   "sound",   "NAS"    },    /*** NO DEFAULT ***/
+   { &soundModule,   "sound",   "OSS"    },
++  { &soundModule,   "sound",   "sndio"  },
+   { &soundModule,   "sound",   "custom" },    /*** NO DEFAULT ***/
+   /* defaults */
+   { &displayModule, "display", "Quartz" },
+@@ -1329,17 +1326,6 @@ int main(int argc, char **argv, char **envp)
      outOfMemory();
  
    signal(SIGSEGV, sigsegv);
Index: pkg/PLIST
===================================================================
RCS file: /cvs/ports/lang/squeak/vm/pkg/PLIST,v
retrieving revision 1.8
diff -u -p -r1.8 PLIST
--- pkg/PLIST   11 Sep 2016 16:30:12 -0000      1.8
+++ pkg/PLIST   14 Jan 2020 14:59:57 -0000
@@ -1,16 +1,17 @@
 @comment $OpenBSD: PLIST,v 1.8 2016/09/11 16:30:12 ratchov Exp $
 bin/squeak
 lib/squeak/${FULLV}/
-@bin lib/squeak/${FULLV}/B3DAcceleratorPlugin
-@bin lib/squeak/${FULLV}/PseudoTTYPlugin
-@bin lib/squeak/${FULLV}/Squeak3D
-@bin lib/squeak/${FULLV}/UnixOSProcessPlugin
-@bin lib/squeak/${FULLV}/XDisplayControlPlugin
-lib/squeak/${FULLV}/npsqueak.so
+@so lib/squeak/${FULLV}/B3DAcceleratorPlugin.so
+@so lib/squeak/${FULLV}/PseudoTTYPlugin.so
+@so lib/squeak/${FULLV}/Squeak3D.so
+@so lib/squeak/${FULLV}/UnixOSProcessPlugin.so
+@so lib/squeak/${FULLV}/XDisplayControlPlugin.so
+@so lib/squeak/${FULLV}/npsqueak.so
 @bin lib/squeak/${FULLV}/squeak
-@bin lib/squeak/${FULLV}/vm-display-X11
-@bin lib/squeak/${FULLV}/vm-display-null
-@bin lib/squeak/${FULLV}/vm-sound-null
+@so lib/squeak/${FULLV}/vm-display-X11.so
+@so lib/squeak/${FULLV}/vm-display-null.so
+@so lib/squeak/${FULLV}/vm-sound-null.so
+@so lib/squeak/${FULLV}/vm-sound-sndio.so
 lib/squeak/npsqueakregister
 lib/squeak/npsqueakrun
 @man man/man1/inisqueak.1

Reply via email to