Lazarus

Programming => Graphics and Multimedia => Audio and Video => Topic started by: robert rozee on April 24, 2020, 06:01:59 pm

Title: alsa beep - linux only
Post by: robert rozee on April 24, 2020, 06:01:59 pm
hi,
    it seems that using ALSA is the way to produce sound on most modern linux distros, it coming pre-installed with most of the mainstream ones.

i'd like to be able to access ALSA, directly from a lazarus/linux application, in order to generate simple tones, of the form:
Code: Pascal  [Select][+][-]
  1. beep(frequency);

however, there seems to be no sample code out there showing how to do this, even though ALSO does appear to include emulation of a 'standard' sound card. i was wondering if any of the experts out there could offer any pointers? note - saving a .wav file to disk and using aplay to play it is not an option, i need to be able to talk directly to ALSA.


cheers,
rob   :-)


Title: Re: alsa beep - linux only
Post by: MarkMLl on April 24, 2020, 06:32:56 pm
Avoid unless you have to. I use MIDI via ALSA for various annunciation stuff, but I have this as a fallback:

Code: [Select]
uses
  xkblib, xlib (* Access to xkb extensions *);


  procedure bell(strokes: integer= 1);

  var
    dpy: PDisplay;

  begin
    dpy := XOpenDisplay(nil);
    try
      XSynchronize(dpy, 1); // If not used output is deferred until XCloseDisplay()
      while strokes > 0 do begin
        xkblib.XkbForceBell(dpy, 100);
        strokes -= 1
      end
    finally
      XCloseDisplay(dpy)
    end
  end { bell } ;

I can't remember exactly where I got to with this stuff. I /think/ ye traditional Write(#$07); only results in audible output on the Linux console (i.e. what you get to using <Ctrl><Alt><F1> etc.), and that the keyboard-extension bell as shown above works in other cases including e.g. KDE Konsole. I don't think I've tried it over VNC, and GOK what happens with Wayland.

MarkMLl
Title: Re: alsa beep - linux only
Post by: robert rozee on April 24, 2020, 07:17:57 pm
Code: Pascal  [Select][+][-]
  1. xkblib.XkbForceBell(dpy, 100);  

yep, i've been playing with some very similar code. it works on some machines, but unfortunately not on others - on my desktop machine it sounds a tiny little buzzer on the motherboard, while on the laptops i've tried it does nothing. using the sound system built into most every computer sold in the last 10 years makes more sense in my application.

on windows, i just use BEEP(dwFrequency, dwDuration) (from the windows unit), seems that microsoft have kept ahead of the ball in that regard!

btw, i'm happy to control the starting and stopping myself, that part isn't a problem.


cheers,
rob   :-)
Title: Re: alsa beep - linux only
Post by: MarkMLl on April 24, 2020, 08:43:18 pm
That works on the laptops I've tried- but not very loud. In the case of ALSA MIDI it obviously assumes the existence of either MIDI hardware or of software emulation, and one has to go through an unpleasant API to get the "true name" of the device. I suspect that a waveform-oriented device would be only marginally better, and one would need to get the waveform from somewhere... either a file or a resource merged into the executable binary.

It might be easier to look for something that triggers a system sound, but I don't know whether there's a standard API for that to cover Gnome, KDE etc... you might find that the best solution is to use xdgopen plus a waveform file and assume (a) that the user has configured his system properly and (b) that the waveform file is correctly bundled with the app.

Later: the documentation for Sox's  play  command is relevant, but I suspect that Sox has to be built to incorporate whatever sound subsystems are on the PC rather than going to the kernel or hardware... I'd love to be told I'm wrong there, and that there's a single low-level interface that can be used for simple cases.

MarkMLl
Title: Re: alsa beep - linux only
Post by: robert rozee on April 25, 2020, 07:01:03 pm
i've found some (relatively short) C code that wasn't too difficult to adapt to the task:

Code: C  [Select][+][-]
  1. /*
  2. adapted from:
  3. https://www.linuxjournal.com/article/6735
  4. see "Listing 3"
  5.  
  6. This example writes an approximated sine wave of
  7. 440Hz to the default PCM device for approx 200ms.
  8. */
  9.  
  10. // Use the newer ALSA API
  11. #define ALSA_PCM_NEW_HW_PARAMS_API
  12.  
  13. #include <alsa/asoundlib.h>
  14.  
  15. int main()
  16. {
  17.   long loops;
  18.   int rc;
  19.   int size;
  20.   snd_pcm_t *handle;
  21.   snd_pcm_hw_params_t *params;
  22.   unsigned int val;
  23.   int dir;
  24.   snd_pcm_uframes_t frames;
  25.   char *buffer;
  26.  
  27. // Open PCM device for playback
  28.   rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
  29.   if (rc < 0)
  30.   {
  31.     fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
  32.     exit(1);
  33.   }
  34.  
  35. // Allocate a hardware parameters object
  36.   snd_pcm_hw_params_alloca(&params);
  37.  
  38. // Fill it in with default values
  39.   snd_pcm_hw_params_any(handle, params);
  40.  
  41. // Set the desired hardware parameters
  42.  
  43. // Interleaved mode
  44.   snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
  45.  
  46. // Signed 16-bit little-endian format
  47.   snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
  48.  
  49. // Two channels (stereo)
  50.   snd_pcm_hw_params_set_channels(handle, params, 2);
  51.  
  52. // 44100 bits/second sampling rate (CD quality)
  53.   val = 44100;
  54.   snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);
  55.  
  56. // Set period size to 256 frames (original 32 frames caused scratches)
  57.   frames = 256;
  58.   snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
  59.  
  60. // Write the parameters to the driver
  61.   rc = snd_pcm_hw_params(handle, params);
  62.   if (rc < 0)
  63.   {
  64.     fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
  65.     exit(1);
  66.   }
  67.  
  68. // Use a buffer large enough to hold one period
  69.   snd_pcm_hw_params_get_period_size(params, &frames, &dir);
  70.  
  71.   size = frames * 4;                                   // 2 bytes/sample, 2 channels
  72.   buffer = (char *) malloc(size);
  73.  
  74. // We want to loop for 200ms seconds
  75.   snd_pcm_hw_params_get_period_time(params, &val, &dir);
  76.  
  77. // 0.2 seconds in microseconds divided by period time
  78.   loops = 200000 / val;
  79.  
  80. //printf("loops = %ld\n", loops);
  81.  
  82.   int I, J=0, K=0, level, phase=0;
  83.  
  84.   while (loops > 0)
  85.   {
  86.     loops--;
  87.  
  88.     for(I=0;I<size;)
  89.     {
  90.       switch (phase)
  91.       {
  92.         case 0: level=  0; break;                      // crude approximation  
  93.         case 1: level= 30; break;                      // of a sine wave  
  94.         case 2: level= 60; break;
  95.         case 3: level= 60; break;
  96.         case 4: level= 30; break;
  97.         case 5: level= -0; break;
  98.         case 6: level=-30; break;
  99.         case 7: level=-60; break;
  100.         case 8: level=-60; break;                      // the number of points
  101.         case 9: level=-30;                             // affects the frequency
  102.       }
  103.  
  104.       J++;
  105.       if (J>10) { phase=((phase+1) % 10); J=0; }       // the J=xx controls frequency
  106. //    printf("phase is %d\n", phase);
  107.  
  108.       if (K<200) { level=(level*K) / 200; }            // ramp up over first 200 samples      
  109.       if (loops>16) K++; else if (K>0) K--;            // and down over last 200+
  110.  
  111. //    printf("K= %d\n", K);
  112.  
  113.       buffer[I++]=0;                                   // low byte
  114.       buffer[I++]=level;                               // high byte
  115.       buffer[I++]=0;                                   // low byte
  116.       buffer[I++]=level;                               // high byte
  117.     }
  118.  
  119.     rc = snd_pcm_writei(handle, buffer, frames);
  120.     if (rc == -EPIPE)                                  // EPIPE means underrun
  121.     {
  122.       fprintf(stderr, "underrun occurred\n");
  123.       snd_pcm_prepare(handle);
  124.     }
  125.     else if (rc < 0)
  126.     {
  127.       fprintf(stderr, "error from writei: %s\n", snd_strerror(rc));
  128.     }  
  129.     else if (rc != (int)frames)
  130.     {
  131.       fprintf(stderr, "short write, write %d frames\n", rc);
  132.     }
  133.   }
  134.  
  135. //printf("residual K = %d\n", K);
  136.  
  137.   snd_pcm_drain(handle);
  138.   snd_pcm_close(handle);
  139.   free(buffer);
  140.  
  141.   return 0;
  142. }
(from :https://www.linuxjournal.com/article/6735)

this can be compiled to a standalone linux executable with:
gcc beep.c -no-pie `pkg-config --cflags --libs alsa`
for reasons unknown to me, the type of quote mark matters!

i've tested it on a couple of linux mint machines (one desktop, one notebook) and it seems to work fine. it does NOT work with EasyOS, producing no sound, but that could be a fault with the distro.

does anyone feel inclined to translate this to pascal? if so, i'd be happy to put a nice wrapper around it that runs the process as a separate thread and just exposes a BEEP (frequency, duration) interface. alas, my own C skills are rather limited.


cheers,
rob   :-)
Title: Re: alsa beep - linux only
Post by: MarkMLl on April 25, 2020, 08:00:09 pm
But that still, unfortunately, carries all the ALSA library baggage around :-(

I think the real question- from my POV at least- is what the minimal code would be that could drive /dev/snd or /dev/dsp (ALSA and OSS respectively I think) at the kernel level, if necessary using a minimal repertoire of beep sounds stored in resources.

this can be compiled to a standalone linux executable with:

gcc beep.c -no-pie `pkg-config --cflags --libs alsa`
for reasons unknown to me, the type of quote mark matters!

The quotes are being processed by the shell. Single quotes pass the contents verbatim (and prevent the command line parser from braking on spaces etc.), double quotes expand $PATH etc., backticks- which is what you've got there- execute the command and pass the text output hence

Code: [Select]
$ pkg-config --cflags --libs alsa
-I/usr/include/alsa -lasound

$ echo `pkg-config --cflags --libs alsa`
-I/usr/include/alsa -lasound

There are other ways of representing those operations, but by the time you need to investigate those you should be seriously considering whether something would be better expressed in Perl etc.

MarkMLl
Title: Re: alsa beep - linux only
Post by: robert rozee on April 26, 2020, 06:42:01 pm
the below seems to work, although the sound quality isn't the best. it is based upon the C example located here, translated into pascal:
https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html (https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html)

Code: Pascal  [Select][+][-]
  1. uses alsa;
  2.  
  3. function ALSAbeep(frequency, duration:integer):boolean;
  4. var buffer:array[0..2400-1] of byte;     // 1/20th of a second worth of samples @48000 bps
  5.     frames:snd_pcm_sframes_t;
  6.        pcm:PPsnd_pcm_t;
  7.       I,LC:integer;
  8.         SA:array[0..359] of byte;
  9.       SS,R:real;
  10. const device='default'+#0;
  11. begin
  12.   result:=false;
  13.   if duration<50 then duration:=50;
  14.   if frequency<20 then frequency:=20;
  15.  
  16.   SS:=(sizeof(SA)/sizeof(buffer))*(frequency/20.0);
  17.   for I:=0 to 359 do SA[I]:=128 + round(sin(pi*I/180.0) * 100.0);    // 100 is effectively the volume
  18.   R:=0.0;
  19.  
  20.   if snd_pcm_open(@pcm, @device[1], SND_PCM_STREAM_PLAYBACK, 0)=0 then
  21.   if snd_pcm_set_params(pcm, SND_PCM_FORMAT_U8,
  22.                              SND_PCM_ACCESS_RW_INTERLEAVED,
  23.                              1,
  24.                              48000,            // bitrate (bps)
  25.                              1,
  26.                              20000)=0 then     // latency (us)
  27.   for LC:=1 to duration div 50 do
  28.   begin
  29.     for I:=0 to sizeof(buffer)-1 do
  30.     begin
  31.       buffer[I]:=SA[trunc(R)];
  32.       R:=R+SS;
  33.       if R>=360.0 then R:=R-360.0
  34.     end;
  35.     frames:=snd_pcm_writei(pcm, @buffer, sizeof(buffer));
  36.     if frames<0 then frames:=snd_pcm_recover(pcm, frames, 0);
  37.     if frames<0 then break  // writeln(snd_strerror(frames))
  38.   end;
  39.   snd_pcm_close(pcm);
  40.   result:=true
  41. end;
  42.  

note, the routine uses the unit alsa, from here:
https://github.com/astoeckel/audorra/blob/master/lib/alsa.pas (https://github.com/astoeckel/audorra/blob/master/lib/alsa.pas)
with the definition for snd_pcm_recover added in from fpAlsa. for anyone wanting to use alas.pas to compile the above, i've attached the modified version.

the code is a tad rough, but does the job. it uses 1 channel, 8-bit unsigned. i tried changing to 8-bit signed, but it didn't work - i'm not sure why. and, of course, it is blocking, but this is easy enough solved in several different ways.

i can't see any reason why lazarus should not now have a LINUX beep function built in, using the ALSA system that seems to be the accepted standard for modern linux distros.


cheers,
rob   :-)

addendum: just improved the sound quality massively by switching from shortints to bytes!
Title: Re: alsa beep - linux only
Post by: MarkMLl on April 29, 2020, 10:17:22 am
Also see http://bulba.untergrund.net/Ay_Emul29b26.src.7z which is a sound-chip emulator (AY-whatever... I am ill at these numbers) apparently written in Lazarus, and https://github.com/tinyalsa/tinyalsa which is a minimal ALSA library (written in C, unfortunately, but possibly a less-demanding prerequisite than the standard one).

The AY chip was a mainstay of 8-bit computers, and if I had the time I'd investigate whether those projects could be merged and provided as a "beep" component.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Thaddy on April 29, 2020, 10:55:50 am
the below seems to work, although the sound quality isn't the best. it is based upon the C example located here, translated into pascal:
https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html (https://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm_min_8c-example.html)
Well, your code works, but the sample rate is chosen too high for many devices. I would suggest 22050.
I would also suggest to simply include a nice Bell wav resource at that sample rate, which can obtain much better results.
Since you only have to store one full cycle (and use repeat), that does not take up too much space.
(I used to write sampler VST plug-ins using that approach)

Windows has nice wav samples at that rate for the system sounds, you can also simply use one of those. (a bit bigger, but the sound is very nice)
Title: Re: alsa beep - linux only
Post by: robert rozee on April 29, 2020, 03:53:33 pm
hi Thaddy,
    i've streamlined things quite a bit now, with just a single include file (have incorporated the bits of alsa.pas and inc.pas that i needed) and a somewhat better management of the streaming. i've also improved the maths, just using integers to select the next sample. threading is now used to allow playing the sound sample without blocking the rest of the application, and a single interface point - BELL, a byte variable - as trigger.

you are right - for just a simple fixed bell sound it can be much simpler. as for the sample rate of 48000, i picked that because i knew no better. am not sure what sample rates ALSA supports for playback across most platforms, the example code i started with used 48000 so i didn't bother changing that.

it is quite possible that if the complete sample is fitted into a single buffer load, then blocking can be disabled (call snd_pcm_open() with the last parameter set to 1) and the bell sound played with just the one call to snd_pcm_writei() followed by a call to snd_pcm_drain(). this would require rolling it all into a unit, with snd_pcm_open() called in the unit startup code and snd_pcm_close() called in the shutdown code.

i've attached a .zip file of the include file i'm now using, as well as including it as a code block below. the code is working quite well with the rest of my application, and i'm pretty happy with the result.


cheers,
rob   :-)


Code: Pascal  [Select][+][-]
  1. ///////////////////////////////////////////////////////////////////////////////////
  2. // remember to add -dUseCThreads to Project -> Project Options -> Custom Options //
  3. ///////////////////////////////////////////////////////////////////////////////////
  4. //
  5. // include this file near the top of your implementation section with:
  6. //
  7. // {$I beeper.inc}
  8. //
  9. // and in your startup code activate the threading with:
  10. //
  11. // TCheckThread.Create(false)
  12. //
  13. // you also need to add -dUseCThreads to the compiler custom options
  14. // for the threading to work. threading is used to allow the ALSAbeep
  15. // routine to function without blocking the rest of your application.
  16. //
  17. // to queue a bell sounding do the following:
  18. //
  19. // if BELL<16 then inc(BELL);
  20. //
  21. // the variable BELL contains the number of queued bell activations,
  22. // hence the placing of an upper limit to stop the sound driving you
  23. // mad if you inadvertentantly queue up too many! the thread decrements
  24. // the value of BELL as each bell sounding is processed, and you can
  25. // check if the bell is currently sounding with:
  26. //
  27. // if BELL<>0 then...
  28. //
  29. ///////////////////////////////////////////////////////////////////////////////////
  30. //
  31. // suggested improvements:
  32. //
  33. // - turn into a unit
  34. // - use a suitable sample rate that is lower than 48000
  35. // - as a simple "bell" can used a pre-encoded sample
  36. // - use non-blocking ALSA calls so doesn't need threading
  37. //
  38. //
  39. // Robert Rozee, 30-April-2020
  40. // rozee@mail.com
  41. //
  42. ///////////////////////////////////////////////////////////////////////////////////
  43.  
  44.  
  45.  
  46. //////////////////////////////////////////////////////////////
  47. // the below ALSA types, constants and functions are copied //
  48. // from the pwm.inc file that is a part of fpAlsa           //
  49. //////////////////////////////////////////////////////////////
  50.  
  51. const
  52.   libasound = 'asound';
  53.  
  54. type
  55.   { Signed frames quantity }
  56. //Psnd_pcm_sframes_t = ^snd_pcm_sframes_t;
  57.   snd_pcm_sframes_t = cint;
  58.  
  59.   { PCM handle }
  60.   PPsnd_pcm_t = ^Psnd_pcm_t;
  61.   Psnd_pcm_t = Pointer;
  62.  
  63.   { PCM stream (direction) }
  64. //Psnd_pcm_stream_t = ^snd_pcm_stream_t;
  65.   snd_pcm_stream_t = cint;
  66.  
  67.   { PCM sample format }
  68. //Psnd_pcm_format_t = ^snd_pcm_format_t;
  69.   snd_pcm_format_t = cint;
  70.  
  71.   { PCM access type }
  72. //Psnd_pcm_access_t = ^snd_pcm_access_t;
  73.   snd_pcm_access_t = cint;
  74.  
  75.   { Unsigned frames quantity }
  76. //Psnd_pcm_uframes_t = ^snd_pcm_uframes_t;
  77.   snd_pcm_uframes_t = cuint;
  78.  
  79. const
  80.     { Playback stream }
  81.     SND_PCM_STREAM_PLAYBACK: snd_pcm_stream_t = 0;
  82.  
  83.     { Unsigned 8 bit }
  84.     SND_PCM_FORMAT_U8: snd_pcm_format_t = 1;
  85.  
  86.     { snd_pcm_readi/snd_pcm_writei access }
  87.     SND_PCM_ACCESS_RW_INTERLEAVED: snd_pcm_access_t = 3;
  88.  
  89. function snd_pcm_open(pcm: PPsnd_pcm_t; name: PChar;
  90.       stream: snd_pcm_stream_t; mode: cint): cint; cdecl; external libasound;
  91.  
  92. function snd_pcm_set_params(pcm: Psnd_pcm_t; format: snd_pcm_format_t;
  93.       access: snd_pcm_access_t; channels, rate: cuint; soft_resample: cint;
  94.       latency: cuint): cint; cdecl; external libasound;
  95.  
  96. function snd_pcm_writei(pcm: Psnd_pcm_t; buffer: Pointer;
  97.       size: snd_pcm_uframes_t): snd_pcm_sframes_t; cdecl; external libasound;
  98.  
  99. function snd_pcm_recover(pcm: Psnd_pcm_t; err, silent: cint): cint; cdecl; external libasound;
  100.  
  101. function snd_pcm_drain(pcm: Psnd_pcm_t): cint; cdecl; external libasound;
  102.  
  103. function snd_pcm_close(pcm: Psnd_pcm_t): cint; cdecl; external libasound;
  104.  
  105. /////////////////////////////////////////////////////////////
  106.  
  107.  
  108.  
  109.  
  110. function ALSAbeep(frequency, duration, volume:integer; warble:boolean):boolean;
  111. var buffer:array[0..9600-1] of byte;           // 1/5th second worth of samples @48000Hz
  112.     frames:snd_pcm_sframes_t;                  // number of frames written (negative if an error occurred)
  113.        pcm:PPsnd_pcm_t;                        // sound device handle
  114.       I,FC:integer;
  115.         SA:array[0..359] of shortint;          // array of sine wave values for a single cycle
  116. const device='default'+#0;                     // name of sound device
  117. var count1,count2,N,X:integer;
  118.  
  119. begin
  120.   result:=false;
  121.  
  122.   if snd_pcm_open(@pcm, @device[1], SND_PCM_STREAM_PLAYBACK, 0)=0 then
  123.   if snd_pcm_set_params(pcm, SND_PCM_FORMAT_U8,
  124.                              SND_PCM_ACCESS_RW_INTERLEAVED,
  125.                              1,                        // number of channels
  126.                              48000,                    // sample rate (Hz)
  127.                              1,                        // resampling on/off
  128.                              500000)=0 then            // latency (us)
  129.   begin
  130.     result:=true;
  131.  
  132.     frequency:=abs(frequency);                                         // -\
  133.     duration:=abs(duration);                                           //   |-- ensure no parameters are negative
  134.     volume:=abs(volume);                                               // -/
  135.     if frequency<20 then frequency:=20;                                // -\
  136.     if duration<50 then duration:=50;                                  //   |-- restrict parameters to usable ranges
  137.     if volume>100 then volume:=100;                                    // -/
  138.  
  139.     for I:=0 to 359 do SA[I]:=round(sin(pi*I/180.0)*volume);           // create sine wave pattern
  140.     X:=0;
  141.     N:=0;                                                              // up/down counter used by unequal interval division
  142.  
  143.     count1:=0;                                                         // count1 counts up, count2 counts down
  144.     count2:=duration*48;                                               // (at 48000Hz there are 48 samples per ms)
  145.  
  146.     while count2>0 do                                                  // start making sound!
  147.     begin
  148.       FC:=0;
  149.       for I:=0 to sizeof(buffer)-1 do                                  // fill buffer with samples
  150.       begin
  151.         if count2>0 then begin
  152.                            if count1<480 then buffer[I]:=128 + ((count1*SA[X]) div 480) else   // 10ms feather in
  153.                            if count2<480 then buffer[I]:=128 + ((count2*SA[X]) div 480) else   // 10ms feather out
  154.                                               buffer[I]:=128 + SA[X];
  155.                            if warble and odd(count1 div 120) then buffer[I]:=128;              // 200Hz warble
  156.                            inc(FC)
  157.                          end
  158.                     else begin
  159.                            buffer[I]:=128;                             // no signal on trailing end of buffer, just in case
  160.                            if (FC mod 2400)<>0 then inc(FC)            // keep increasing FC until is a multiple of 2400
  161.                          end;
  162.  
  163.         inc(N,frequency*360);                                          // unequal interval division routine
  164.         while (N>0) do begin                                           // (a variation on Bresenham's Algorithm)
  165.                          dec(N,48000);
  166.                          inc(X)
  167.                        end;
  168.         X:=X mod 360;
  169.  
  170.         inc(count1);
  171.         dec(count2)
  172.       end;
  173.  
  174.       frames:=snd_pcm_writei(pcm, @buffer, max(2400, FC));             // write AT LEAST one full period
  175.       if frames<0 then frames:=snd_pcm_recover(pcm, frames, 0);        // try to recover from any error
  176.       if frames<0 then break                                           // give up if failed to recover
  177.     end;
  178.     snd_pcm_drain(pcm);                                                // drain any remaining samples
  179.     snd_pcm_close(pcm)
  180.   end
  181. end;
  182.  
  183.  
  184. ///////////////////////////////////////////////////////////////////////////////////
  185.  
  186.  
  187. const BELL:byte=0;                             // increment to sound bell
  188.                                                // (use a byte to ensure is atomic)
  189.  
  190. ///////////////////////////////////////////////////////////////////////////////////
  191.  
  192.  
  193. type TCheckThread = class(TThread)
  194.      private
  195.      protected
  196.        procedure Execute; override;
  197.      end;
  198.  
  199. // separate thread used to check for command to activate bell
  200. procedure TCheckThread.Execute;
  201. begin
  202.   while true do
  203.   begin
  204.     if BELL>0 then begin
  205. //                   ALSAbeep(440, 100, 100, false);           // basic bell sound
  206.                      ALSAbeep(420, 100, 100, true);            // fancy bell sound
  207.                      dec(BELL)
  208.                    end
  209.               else sleep(100)
  210.   end
  211. end;
  212.  

addendum 23-may-2020:
note that for Lazarus to be able to link to the ALSA routines, you need to have installed the package libasound2-dev. this is achieved with the following typed at a terminal window:
Code: Bash  [Select][+][-]
  1. sudo apt-get install libasound2-dev

it looks like linux mint 19.3 does not have libasound2-dev pre-installed, but the latest raspbian does.
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 12:43:30 pm
addendum 23-may-2020:
note that for Lazarus to be able to link to the ALSA routines, you need to have installed the package libasound2-dev. this is achieved with the following typed at a terminal window:
Code: Bash  [Select][+][-]
  1. sudo apt-get install libasound2-dev

it looks like linux mint 19.3 does not have libasound2-dev pre-installed, but the latest raspbian does.

To avoid to oblige people to install the dev package, you may use dynamic loading:

Code: Pascal  [Select][+][-]
  1.  loadlibrary('libasound.so.2');

and define the methods used in the main unit.



Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 12:52:19 pm
Pretty much necessary if it's to be hung on OnBeep, but in fairness he did make clear that it was a proof-of-concept and he's done good work finding a suitably light ALSA binding.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 01:13:12 pm
Pretty much necessary if it's to be hung on OnBeep, but in fairness he did make clear that it was a proof-of-concept and he's done good work finding a suitably light ALSA binding.

MarkMLl

Yes, of course, I was thinking about a lcl_beep(), from previous discussion, it is clear that a sysutils.OnBeep for Linux has no candidate.

But the work of Robert Rozee is excellent and in my opinion, the best candidate for a gui_onbeep.

MSEgui for his gui_onbeep uses XBell(para1:PDisplay; para2:cint) from xlib.pp.

But I have to admit that it does not work on Debian for example.
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 01:30:14 pm
Yes, of course, I was thinking about a lcl_beep(), from previous discussion, it is clear that a sysutils.OnBeep for Linux has no candidate.

I'd propose this with dynamic loading to be hung on OnBoot by TApplication.

Quote
MSEgui for his gui_onbeep uses XBell(para1:PDisplay; para2:cint) from xlib.pp.

But I have to admit that it does not work on Debian for example.

This works here on Debian, but isn't very loud

Code: [Select]
procedure bell(strokes: integer= 1);

var
  dpy: PDisplay;

begin
  dpy := XOpenDisplay(nil);     (* Sensitive to "out of file descriptors" etc.  *)
  if dpy <> nil then
    try
      XSynchronize(dpy, 1); // If not used output is deferred until XCloseDisplay()
      while strokes > 0 do begin
        xkblib.XkbForceBell(dpy, 100);
        strokes -= 1
      end
    finally
      XCloseDisplay(dpy)
    end
end { bell } ;

I've rather got my hands full (DST transitions are a lot of work) but am currently trying to find a reliable determinator between a GUI program running under e.g. Konsole and one running under e.g. Xterm. In the former case I think it would be preferable to use a simple  Write(#$07)  since that is the only way I've found so far to get it controlled by the desktop environment's theme settings.

Historically, when I first looked at migrating a DOS program to Windows I noted that a PS/2-77 beep was 900Hz 660mSec, I prefer to round that to a standard note hence 880Hz.

MarkMLl

Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 01:59:14 pm
Quote
This works here on Debian, but isn't very loud:
...


This tested on Debian 10.1 64 bit on i5, it is very not loud even not louder than silence.

 ;)
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 02:03:47 pm
This tested on Debian 10.1 64 bit on i5, it is very not loud even not louder than silence.

 ;)

That's actually more helpful than the twit on StackOverflow who insisted that a mainboard beeper was always redirected to ALSA so \a would work in all cases. And then got stroppy.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 02:16:20 pm
Quote
That's actually more helpful than the twit on StackOverflow who insisted that a mainboard beeper was always redirected to ALSA so \a would work in all cases...

Huh, I did not follow that battle but this make me think to something.

I will check my bios and I think that the system-beep was set as disabled.

I will enable it and maybe continue the battle.

Write you later.
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 02:31:19 pm
Quote
Write you later

Ok, system beep enabled in bios.

But even with this, on Debian 10.1, 64 bit, on i5 from a ThinkPad X390, I can only ear the click of the button or the clack of the key pressed but nothing else.

This using XkbForceBell or XBell.



Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 02:42:42 pm
Does  xkbbell  work?

If I continue the discussion anywhere it's likely to be the XDG mailing list, since I'd expect there to be people there from the major desktops and distreaux.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 02:46:42 pm
Quote
Does  xkbbell  work?

Do you mean the executable?

This works here in perfect silence:

Code: Pascal  [Select][+][-]
  1. $ xkbbell

Code: Pascal  [Select][+][-]
  1.  $ xkbbell -version
  2.     xkbbell (xkbutils) 1.0.4

Same silence with this:

Code: Pascal  [Select][+][-]
  1. $  xkbbell -force

Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 02:51:02 pm
If you run on the system console (via <Ctrl><Alt><F1> etc.) does  echo -ne '\a'  beep?

If it doesn't, it's a kernel/hardware issue. If it does, it's a desktop/X11 issue.

MarkMLl

ps If you're not used to that, it's usually <Ctrl><Alt><F1> to get back to your desktop.
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 03:05:24 pm
If you run on the system console (via <Ctrl><Alt><F1> etc.) does  echo -ne '\a'  beep?

It does the same noise: silent.

Quote
If it doesn't, it's a kernel/hardware issue.

Hum, yes maybe, but when some applications are not happy, like xfce4-terminal, it can produce a beep.

Anyway, XkbForceBell or XBell will not be available with Wayland.
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 03:09:26 pm
If you run on the system console (via <Ctrl><Alt><F1> etc.) does  echo -ne '\a'  beep?

It does the same noise: silent.

Quote
If it doesn't, it's a kernel/hardware issue.

Hum, yes maybe, but when some applications are not happy, like xfce4-terminal, it can produce a beep.

Which I suspect you will find is via libnotify and ALSA, and I suspect that in that particular case the shell will also respond to \a

What I've not found out is how to duplicate that behaviour from an app, and that's an XDG question.

Quote
Anyway, XkbForceBell or XBell be will not be available with Wayland.

True, although I think that that's on their list of things that must be reinstated in the medium term.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 04:53:49 pm
Quote
Which I suspect you will find is via libnotify and ALSA, and I suspect that in that particular case the shell will also respond to \a

Here it seems that it was a problem with PulseAudio.
By default, audible-bell (beep redirection) is not set, this must be added at end of /etc/pulse/default.pa:

Code: Pascal  [Select][+][-]
  1. # audible bell
  2. load-sample-lazy x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
  3. load-module module-x11-bell sample=x11-bell

Also to enable beep and echo -e '\a' in terminal, you need in ~/.config/xfce4/terminal/terminalrc to enable MiscBell to true.

Now indeed, beep, echo -e '\a', xkbbell work on terminal.

And... with fpc, xbell works too (but no success with XkbForceBell).

All that said that I am not sure that using XkbForceBell or xbell will be out-of-the-box for everybody.


Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 05:58:04 pm
Quote
Which I suspect you will find is via libnotify and ALSA, and I suspect that in that particular case the shell will also respond to \a

Here it seems that it was a problem with PulseAudio.
By default, audible-bell (beep redirection) is not set, this must be added at end of /etc/pulse/default.pa:

Code: Pascal  [Select][+][-]
  1. # audible bell
  2. load-sample-lazy x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
  3. load-module module-x11-bell sample=x11-bell

Confirmed that that's not set for Debian "Buster" with KDE. But XkbForceBell() still works :-/

Quote
Also to enable beep and echo -e '\a' in terminal, you need in ~/.config/xfce4/terminal/terminalrc to enable MiscBell to true.

Now indeed, beep, echo -e '\a', xkbbell work on terminal.

And... with fpc, xbell works too (but no success with XkbForceBell).

All that said that I am not sure that using XkbForceBell or xbell will be out-of-the-box for everybody.

Definitely too much hassle for most. I mean, I pride myself on being a fairly high-end nerd but all this grief with BIOS settings, files in /etc. possibly a notification daemon, desktop/wm configuration in different places... it's really no joke.

MarkML
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 06:43:11 pm
Quote
Definitely too much hassle for most. I mean, I pride myself on being a fairly high-end nerd but all this grief with BIOS settings, files in /etc. possibly a notification daemon, desktop/wm configuration in different places... it's really no joke.

Yes, I fear that the solution of Robert Rozee is the best.
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 07:16:32 pm
Quote
But XkbForceBell() still works :-/

Hum, strange, here it does not work.

I noted too that on terminal:

Code: Pascal  [Select][+][-]
  1. $ xkbbell

produce sound but this:

Code: Pascal  [Select][+][-]
  1. $ xkbbell -force

Gives silent.

May I ask if Xbell() is working for you in pascal?

Code: Pascal  [Select][+][-]
  1. program gui_beep;
  2.  
  3. uses
  4. xlib;
  5.  
  6. var
  7.   dpy: PDisplay;
  8. begin
  9.  dpy := XOpenDisplay(nil);
  10.  XSynchronize(dpy, 1);
  11.  xbell(dpy,0);
  12.  xflush(dpy);
  13.  XCloseDisplay(dpy);
  14. end.
  15.  

Thanks.

(Here it works)

Fre;D
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 25, 2020, 09:44:14 pm
The code I posted earlier https://forum.lazarus.freepascal.org/index.php/topic,49502.msg381874.html#msg381874 is running in production.

xkbbell is working both with and without -force... make sure you're not doing something like trying to sudo it.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 25, 2020, 10:56:48 pm
The code I posted earlier https://forum.lazarus.freepascal.org/index.php/topic,49502.msg381874.html#msg381874 is running in production.

xkbbell is working both with and without -force... make sure you're not doing something like trying to sudo it.

No, sudo was not used.

Anyway, I did adapt Robert Rozee code to laod dynamically libasound.so.2 and merge it as unit:
Code: Pascal  [Select][+][-]
  1.  // Original beeper.inc of
  2.  // Robert Rozee, 30-April-2020
  3.  // rozee@mail.com
  4.  
  5.  // Dynamic loading
  6.  // FredvS fiens@hotmail.com
  7.  
  8.  
  9. unit alsa_beep;
  10.  
  11. {$mode objfpc}{$H+}
  12. {$PACKRECORDS C}
  13.  
  14. interface
  15.  
  16. uses
  17.   dynlibs,
  18.   CTypes,
  19.   Math;
  20.  
  21. type
  22.   { Signed frames quantity }
  23.   snd_pcm_sframes_t = cint;
  24.  
  25.   { PCM handle }
  26.   PPsnd_pcm_t = ^Psnd_pcm_t;
  27.   Psnd_pcm_t  = Pointer;
  28.  
  29.   { PCM stream (direction) }
  30.   snd_pcm_stream_t = cint;
  31.  
  32.   { PCM sample format }
  33.   snd_pcm_format_t = cint;
  34.  
  35.   { PCM access type }
  36.   snd_pcm_access_t = cint;
  37.  
  38.   { Unsigned frames quantity }
  39.   snd_pcm_uframes_t = cuint;
  40.  
  41. const
  42.   { Playback stream }
  43.   SND_PCM_STREAM_PLAYBACK: snd_pcm_stream_t = 0;
  44.  
  45.   { Unsigned 8 bit }
  46.   SND_PCM_FORMAT_U8: snd_pcm_format_t       = 1;
  47.  
  48.   { snd_pcm_readi/snd_pcm_writei access }
  49.   SND_PCM_ACCESS_RW_INTERLEAVED: snd_pcm_access_t = 3;
  50.  
  51.   // Dynamic load : Vars that will hold our dynamically loaded functions...
  52.   // *************************** Alsa Methods *******************************
  53.  
  54. var
  55.   snd_pcm_open: function(pcm: PPsnd_pcm_t; Name: PChar; stream: snd_pcm_stream_t; mode: cint): cint; cdecl;
  56.  
  57. var
  58.   snd_pcm_set_params: function(pcm: Psnd_pcm_t; format: snd_pcm_format_t; access: snd_pcm_access_t; channels, rate: cuint; soft_resample: cint; latency: cuint): cint; cdecl;
  59.  
  60. var
  61.   snd_pcm_writei: function(pcm: Psnd_pcm_t; buffer: Pointer; size: snd_pcm_uframes_t): snd_pcm_sframes_t; cdecl;
  62.  
  63. var
  64.   snd_pcm_recover: function(pcm: Psnd_pcm_t; err, silent: cint): cint; cdecl;
  65.  
  66. var
  67.   snd_pcm_drain: function(pcm: Psnd_pcm_t): cint; cdecl;
  68.  
  69. var
  70.   snd_pcm_close: function(pcm: Psnd_pcm_t): cint; cdecl;
  71.  
  72. {Special function for dynamic loading of lib ...}
  73.  
  74. var
  75.   ab_Handle: TLibHandle = dynlibs.NilHandle; // this will hold our handle for the lib; it functions nicely as a mutli-lib prevention unit as well...
  76.  
  77. var
  78.   ReferenceCounter: cardinal = 0;  // Reference counter
  79.  
  80. function ab_IsLoaded: Boolean; inline;
  81.  
  82. function ab_Load: Boolean; // load the lib
  83.  
  84. procedure ab_Unload();     // unload and frees the lib from memory : do not forget to call it before close application.
  85.  
  86. function ALSAbeep(frequency, duration, volume: integer; warble: Boolean): Boolean;
  87.  
  88. implementation
  89.  
  90. function ab_IsLoaded: Boolean;
  91. begin
  92.   Result := (ab_Handle <> dynlibs.NilHandle);
  93. end;
  94.  
  95. function ab_Load: Boolean; // load the lib
  96. var
  97.   thelib: string = 'libasound.so.2';
  98. begin
  99.   Result := False;
  100.   if ab_Handle <> 0 then
  101.   begin
  102.     Inc(ReferenceCounter);
  103.     Result := True; {is it already there ?}
  104.   end
  105.   else
  106.   begin {go & load the library}
  107.     ab_Handle := DynLibs.SafeLoadLibrary(thelib); // obtain the handle we want
  108.     if ab_Handle <> DynLibs.NilHandle then
  109.     begin {now we tie the functions to the VARs from above}
  110.  
  111.       Pointer(snd_pcm_open)       := DynLibs.GetProcedureAddress(ab_Handle, PChar('snd_pcm_open'));
  112.       Pointer(snd_pcm_set_params) := DynLibs.GetProcedureAddress(ab_Handle, PChar('snd_pcm_set_params'));
  113.       Pointer(snd_pcm_writei)     := DynLibs.GetProcedureAddress(ab_Handle, PChar('snd_pcm_writei'));
  114.       Pointer(snd_pcm_recover)    := DynLibs.GetProcedureAddress(ab_Handle, PChar('snd_pcm_recover'));
  115.       Pointer(snd_pcm_recover)    := DynLibs.GetProcedureAddress(ab_Handle, PChar('snd_pcm_recover'));
  116.       Pointer(snd_pcm_drain)      := DynLibs.GetProcedureAddress(ab_Handle, PChar('snd_pcm_drain'));
  117.       Pointer(snd_pcm_close)      := DynLibs.GetProcedureAddress(ab_Handle, PChar('snd_pcm_close'));
  118.  
  119.     end;
  120.     Result           := ab_IsLoaded;
  121.     ReferenceCounter := 1;
  122.   end;
  123.  
  124. end;
  125.  
  126. procedure ab_Unload();
  127. begin
  128.   // < Reference counting
  129.   if ReferenceCounter > 0 then
  130.     Dec(ReferenceCounter);
  131.   if ReferenceCounter > 0 then
  132.     Exit;
  133.   // >
  134.   if ab_IsLoaded then
  135.   begin
  136.     DynLibs.UnloadLibrary(ab_Handle);
  137.     ab_Handle := DynLibs.NilHandle;
  138.   end;
  139. end;
  140.  
  141.  
  142. function ALSAbeep(frequency, duration, volume: integer; warble: Boolean): Boolean;
  143. var
  144.   buffer: array[0..9600 - 1] of byte;           // 1/5th second worth of samples @48000Hz
  145.   frames: snd_pcm_sframes_t;                    // number of frames written (negative if an error occurred)
  146.   pcm: PPsnd_pcm_t;                             // sound device handle
  147.   I, FC: integer;
  148.   SA: array[0..359] of shortint;                // array of sine wave values for a single cycle
  149. const
  150.   device = 'default' + #0;                      // name of sound device
  151. var
  152.   count1, count2, N, X: integer;
  153. begin
  154.   Result := False;
  155.  
  156.   ab_Load;
  157.  
  158.   if snd_pcm_open(@pcm, @device[1], SND_PCM_STREAM_PLAYBACK, 0) = 0 then
  159.     if snd_pcm_set_params(pcm, SND_PCM_FORMAT_U8,
  160.       SND_PCM_ACCESS_RW_INTERLEAVED,
  161.       1,                        // number of channels
  162.       48000,                    // sample rate (Hz)
  163.       1,                        // resampling on/off
  164.       500000) = 0 then            // latency (us)
  165.     begin
  166.       Result := True;
  167.  
  168.       frequency := abs(frequency);                                         // -\
  169.       duration  := abs(duration);                                           //   |-- ensure no parameters are negative
  170.       volume    := abs(volume);                                               // -/
  171.       if frequency < 20 then
  172.         frequency := 20;                                 // -\
  173.       if duration < 50 then
  174.         duration := 50;                                  //   |-- restrict parameters to usable ranges
  175.       if volume > 100 then
  176.         volume   := 100;                                    // -/
  177.  
  178.       for I := 0 to 359 do
  179.         SA[I] := round(sin(pi * I / 180.0) * volume);           // create sine wave pattern
  180.       X       := 0;
  181.       N       := 0;                                                              // up/down counter used by unequal interval division
  182.  
  183.       count1 := 0;                                                         // count1 counts up, count2 counts down
  184.       count2 := duration * 48;                                               // (at 48000Hz there are 48 samples per ms)
  185.  
  186.       while count2 > 0 do                                                  // start making sound!
  187.       begin
  188.         FC    := 0;
  189.         for I := 0 to sizeof(buffer) - 1 do                                  // fill buffer with samples
  190.         begin
  191.           if count2 > 0 then
  192.           begin
  193.             if count1 < 480 then
  194.               buffer[I] := 128 + ((count1 * SA[X]) div 480)
  195.             else   // 10ms feather in
  196.             if count2 < 480 then
  197.               buffer[I] := 128 + ((count2 * SA[X]) div 480)
  198.             else   // 10ms feather out
  199.               buffer[I] := 128 + SA[X];
  200.             if warble and odd(count1 div 120) then
  201.               buffer[I] := 128;              // 200Hz warble
  202.             Inc(FC);
  203.           end
  204.           else
  205.           begin
  206.             buffer[I] := 128;                             // no signal on trailing end of buffer, just in case
  207.             if (FC mod 2400) <> 0 then
  208.               Inc(FC);            // keep increasing FC until is a multiple of 2400
  209.           end;
  210.  
  211.           Inc(N, frequency * 360);                                          // unequal interval division routine
  212.           while (N > 0) do
  213.           begin                                           // (a variation on Bresenham's Algorithm)
  214.             Dec(N, 48000);
  215.             Inc(X);
  216.           end;
  217.           X := X mod 360;
  218.  
  219.           Inc(count1);
  220.           Dec(count2);
  221.         end;
  222.  
  223.         frames   := snd_pcm_writei(pcm, @buffer, max(2400, FC));             // write AT LEAST one full period
  224.         if frames < 0 then
  225.           frames := snd_pcm_recover(pcm, frames, 0);        // try to recover from any error
  226.         if frames < 0 then
  227.           break;                                            // give up if failed to recover
  228.       end;
  229.       snd_pcm_drain(pcm);                                                // drain any remaining samples
  230.       snd_pcm_close(pcm);
  231.     end;
  232.   ab_unload;
  233. end;
  234.  
  235. end.


And the main program:

Code: Pascal  [Select][+][-]
  1. program alsa_beep_test;
  2.  
  3. uses
  4. alsa_beep;
  5.  
  6. begin
  7. ALSAbeep(440, 100, 100, false);  
  8. end.

Here it works out of the box.
Title: Re: alsa beep - linux only
Post by: winni on October 25, 2020, 11:32:34 pm
Hi!

There are a lot of boards around where the manufacturer is to greedy to install a beeper.
So they just switch the BIOS option to disabled.

Whatever you do: then there is no beeper.

xkbbell, XkbForceBell and XBell rely on the existence of a beeper.

So after a long discussion is again there where I started it:
With the support of Alsa.

Only that now Aplay is avoided.

The solution of Fred vS and Robert Rozee works out of the box.
Finally we got a nice and usefull unit.

Thanx!

Winni
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 26, 2020, 09:10:10 am
Only that now Aplay is avoided.

The solution of Fred vS and Robert Rozee works out of the box.
Finally we got a nice and usefull unit.

Which was the point I was trying to make. I never said I was against ALSA, what I didn't like was (a) having to invoke aplay and apply it to a media file (neither of which can be relied on to exist in the general case) and (b) the heavyweight alsapas library.

I'm still looking at whether it's possible to decide whether a (GUI) program has been invoked in a way that means that a bell through Write() will work. However there's a problem here that (i) it relies on Konsole or something similar as a parent and (b) something can be interposed between Konsole and the program which prevents the bell getting through to Konsole (hence the theme handlers and ALSA) without leaving any trace (e.g. all of the XDG environment variables are still defined). I'll ask on the XDG mailing list when I have time (possibly later today) but for the moment the best I can offer is that if an app's parent is a shell and its grandparent Konsole etc. then in principle the user can set up Write(Bell) to go via the themes to ALSA, and if it's not then Write(Bell) will probably be silent.

It certainly appears that the X11 bell is too erratic to be useful.

MarkMLl
Title: Re: alsa beep - linux only
Post by: PascalDragon on October 26, 2020, 09:19:41 am
There are a lot of boards around where the manufacturer is to greedy to install a beeper.
So they just switch the BIOS option to disabled.

Most boards still have the speaker header and nothing is switched off in the BIOS. Only Laptops might have different solutions (e.g. in mine from ~2005 even POST Beeps are routed through the soundcard).

xkbbell, XkbForceBell and XBell rely on the existence of a beeper.

No, they don't. As always it's a question of how the system (in this case both X11 and the sound system) is configured.
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 26, 2020, 09:42:25 am
No, they don't. As always it's a question of how the system (in this case both X11 and the sound system) is configured.

And discussion of this gets back to one of the things that (IMO and with respect to Winni) made aplay such a bad idea: far too much depends on how well the distro is put together, and on /exactly/ what packages have been installed by the system owner.

MarkMLl
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 26, 2020, 12:10:11 pm
Attached is my current take on this. It's got both static and dynamic libraries (although in practice dynamic is vastly preferable- thanks Fred), it's got Robert's original demo function (with some tweaks I did yesterday evening between other things) plus Fred's which has some extra frills. Lazarus project plus simple .pas file which compiles standalone.

Tested on Debian "Buster" (stable) x86_64 KDE.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 26, 2020, 12:48:38 pm
Attached is my current take on this. It's got both static and dynamic libraries (although in practice dynamic is vastly preferable- thanks Fred), it's got Robert's original demo function (with some tweaks I did yesterday evening between other things) plus Fred's which has some extra frills. Lazarus project plus simple .pas file which compiles standalone.

Tested on Debian "Buster" (stable) x86_64 KDE.

MarkMLl

Hello.

Nice.

Note that with the static loading version, due to a very sad restriction of fpc, you will oblige people to install the libasound-dev package or create a symlink  (libasound.so --> libasound.so.2).
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 26, 2020, 01:06:58 pm
Nice.

Note that with the static loading version, due to a very sad restriction of fpc, you will oblige people to install the libasound-dev package or create a symlink  (libasound.so --> libasound.so.2).

Thanks for the appreciative comment. This has generated a painful amount of grief, and it would be nice to get it sorted out.

Is the symlink problem really down to FPC, or is it really a distreaux issue since some (but not necessarily all) don't create it until the -dev is installed? This sort of thing has caused a lot of problems with- in particular- database libraries in the past, and it would be nice if there were some way of sorting it out for good.

Obviously in the current case the dynamic variant is preferable, and TBH I'd prefer to put (my derivative of) Robert's function on there since it's sufficiently simple that anybody who's wavering on linking to ALSA can't claim that we're hiding anything dubious.

This has been a very useful exercise, and I might see if I can do anything comparable for the ALSA MIDI functions. The usual alsapas library is pretty horrid, when setting it up you have to run a script so it can work out what version of the ALSA libraries/headers are on the development system... which have to match the target system.

I think the more people that can test the current code on different distreaux and versions the better.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 26, 2020, 01:12:35 pm
Quote
Is the symlink problem really down to FPC, or is it really a distreaux issue since some (but not necessarily all) don't create it until the -dev is installed? This sort of thing has caused a lot of problems with- in particular- database libraries in the past, and it would be nice if there were some way of sorting it out for good.

https://bugs.freepascal.org/view.php?id=32367

More than 3 years ago already, time goes fast.

 :'(
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 26, 2020, 01:59:35 pm
Quote
Is the symlink problem really down to FPC, or is it really a distreaux issue since some (but not necessarily all) don't create it until the -dev is installed? This sort of thing has caused a lot of problems with- in particular- database libraries in the past, and it would be nice if there were some way of sorting it out for good.

https://bugs.freepascal.org/view.php?id=32367

More than 3 years ago already, time goes fast.

I've been here before, but I can't remember in what context. Any build-time linkage to a .so.n file is /wrong/, and I don't care if it is "the unix way". And having the linker resolve a symlink to an actual .so is also /wrong/: this should be done at load time, and if the OS can't mediate an API negotiation between the app and a library then that is the time it should complain.

But in reality, I think that the "missing symlink" problem is a distro issue.

MarkMLl

Title: Re: alsa beep - linux only
Post by: winni on October 26, 2020, 10:02:33 pm
@Fred vS

Hi!

After reading the code of unit alsa_beep I could not resist to create a nice enhancement:

I created
Code: Pascal  [Select][+][-]
  1. function ALSAglide(StartFreq,EndFreq, duration, volume: integer): Boolean;

The  musicians call it glissando.
The Frequency is moving from StartFreq to EndFreq in duration time.

As your code is very well structured I only hat to add 2 variables and 3 lines of code.
And I simplified the range checking.

Winni

Code: Pascal  [Select][+][-]
  1. function ALSAglide(StartFreq,EndFreq, duration, volume: integer): Boolean;
  2. var
  3.   buffer: array[0..9600 - 1] of byte;           // 1/5th second worth of samples @48000Hz
  4.   frames: snd_pcm_sframes_t;                    // number of frames written (negative if an error occurred)
  5.   pcm: PPsnd_pcm_t;                             // sound device handle
  6.   I, FC: integer;
  7.   SA: array[0..359] of shortint;                // array of sine wave values for a single cycle
  8. const
  9.   device = 'default' + #0;                      // name of sound device
  10. var
  11.   count1, count2, N, X: integer;
  12.   DeltaStep: single;   //winni
  13.   delta : Integer;     //  "
  14. begin
  15.   Result := False;
  16.  
  17.   ab_Load;
  18.  
  19.   if snd_pcm_open(@pcm, @device[1], SND_PCM_STREAM_PLAYBACK, 0) = 0 then
  20.     if snd_pcm_set_params(pcm, SND_PCM_FORMAT_U8,
  21.       SND_PCM_ACCESS_RW_INTERLEAVED,
  22.       1,                        // number of channels
  23.       48000,                    // sample rate (Hz)
  24.       1,                        // resampling on/off
  25.       500000) = 0 then            // latency (us)
  26.     begin
  27.       Result := True;
  28.       StartFreq:= EnsureRange(abs(StartFreq),20,20000);
  29.       EndFreq  := EnsureRange(abs(EndFreq),20,20000);
  30.       duration := EnsureRange(abs(duration),50,maxint);// 24.85 days
  31.       volume   := EnsureRange(abs(Volume),0,100);
  32.       // 48 samples per ms -->
  33.       // 360 / 48 = 7.5
  34.       DeltaStep := 7.5*(EndFreq - startFreq) /(duration);   // winni
  35.  
  36.       for I := 0 to 359 do
  37.         SA[I] := round(sin(pi * I / 180.0) * volume);           // create sine wave pattern
  38.       X       := 0;
  39.       N       := 0;                                                              // up/down counter used by unequal interval division
  40.  
  41.       count1 := 0;                                                         // count1 counts up, count2 counts down
  42.       count2 := duration * 48;                                               // (at 48000Hz there are 48 samples per ms)
  43.  
  44.       while count2 > 0 do                                                  // start making sound!
  45.       begin
  46.         FC    := 0;
  47.         for I := 0 to sizeof(buffer) - 1 do                                  // fill buffer with samples
  48.         begin
  49.           if count2 > 0 then
  50.           begin
  51.             if count1 < 480 then
  52.               buffer[I] := 128 + ((count1 * SA[X]) div 480)
  53.             else   // 10ms feather in
  54.             if count2 < 480 then
  55.               buffer[I] := 128 + ((count2 * SA[X]) div 480)
  56.             else   // 10ms feather out
  57.               buffer[I] := 128 + SA[X];
  58.             Inc(FC);
  59.           end
  60.           else
  61.           begin
  62.             buffer[I] := 128;                             // no signal on trailing end of buffer, just in case
  63.             if (FC mod 2400) <> 0 then
  64.               Inc(FC);            // keep increasing FC until is a multiple of 2400
  65.           end;
  66.  
  67.          delta := round (Count1*DeltaStep);   // winni
  68.          Inc(N,StartFreq*360+Delta);          // winni
  69.           while (N > 0) do
  70.           begin                                           // (a variation on Bresenham's Algorithm)
  71.             Dec(N, 48000);
  72.             Inc(X);
  73.           end;
  74.           X := X mod 360;
  75.  
  76.           Inc(count1);
  77.           Dec(count2);
  78.         end;
  79.  
  80.         frames   := snd_pcm_writei(pcm, @buffer, max(2400, FC));             // write AT LEAST one full period
  81.         if frames < 0 then
  82.           frames := snd_pcm_recover(pcm, frames, 0);        // try to recover from any error
  83.         if frames < 0 then
  84.           break;                                            // give up if failed to recover
  85.       end;
  86.       snd_pcm_drain(pcm);                                   // drain any remaining samples
  87.       snd_pcm_close(pcm);
  88.     end;
  89.   ab_unload;
  90. end; //AlsaGlide
  91.  
  92.  
     
Title: Re: alsa beep - linux only
Post by: Fred vS on October 26, 2020, 10:10:08 pm
@Winni: EXCELLENT.

Do you have a GitHub account?

If yes, it would be good to add it.

If no, if you agree, I will add it to this (and maybe rename into alsa_sound, adding some fast methods, like alsa_beep, alsa_glide, alsa_blabla, etc...):

https://github.com/fredvs/alsa_beep

[EDIT]
Quote
As your code is very well structured I only hat to add 2 variables and 3 lines of code.

Huh, it is the code of Robert Rozee and it shows a highest skill.
@Robert Rozee many thanks for your providential perfect code.

Fre;D

Title: Re: alsa beep - linux only
Post by: winni on October 26, 2020, 10:29:58 pm
@Fred vS

Hi!

No, I don't have a GitHub account.

Add it to your unit!
And now I know how to handle the Alsa lib!

Winni
Title: Re: alsa beep - linux only
Post by: Fred vS on October 26, 2020, 10:36:02 pm
Hello Winni.

Quote
Add it to your unit!

Yep, thanks!

Quote
And now I know how to handle the Alsa lib!

Yes, me too and this thanks to Robert Rozee.

Many, many wow and thanks Rob!

Fre;D
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 27, 2020, 12:13:24 pm
I'd suggest that while vibrato, glissando etc. are great fun, they're outside the remit of getting a (Lazarus) program to beep on Linux.

It does suggest however that there's a lot of merit in the idea of exposing Fred's modification of Robert's consolidation of (whoever's) cut-down ALSA interface library so that other programs can use it.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 27, 2020, 12:31:25 pm
Quote
I'd suggest that while vibrato, glissando etc..

Yes good idea.

I added ALSAbeeperStereo(), a stereo beeper with custom frequency for each channel.

https://github.com/fredvs/alsa_sound

Fre;D
Title: Re: alsa beep - linux only
Post by: lucamar on October 27, 2020, 12:40:14 pm
https://github.com/fredvs/alsa_sound

Fre;D

Totally off-topic and, please, excuse me for that but in your repo, what's dummy.txt for?
Title: Re: alsa beep - linux only
Post by: Fred vS on October 27, 2020, 12:57:15 pm
https://github.com/fredvs/alsa_sound

Fre;D

Totally off-topic and, please, excuse me for that but in your repo, what's dummy.txt for?

Huh, because I did not find a other way with GitHub to not remove empty directories after a git push.
I prefer to give the sub-directory /units in source so people dont have to create it when using this command line:

Code: Bash  [Select][+][-]
  1. $ fpc -FUunits alsa_sound_test.pas
Title: Re: alsa beep - linux only
Post by: PascalDragon on October 27, 2020, 03:13:38 pm
Huh, because I did not find a other way with GitHub to not remove empty directories after a git push.

That is because Git tracks files not paths (like SVN does). If there is no file in a directory then it does not exist from Git's point of view.
Title: Re: alsa beep - linux only
Post by: MarkMLl on October 27, 2020, 04:47:54 pm
I've sent the .zip I uploaded yesterday to Mantis as https://bugs.freepascal.org/view.php?id=37997

If FPC accepts it as a package then possibly Lazarus/LCL would incorporate a function calling it. I see non-GUI programs as less of an issue since it is reasonable for a shell to process a bell character.

I'll try to raise related issues on the XDG mailing list when I have time.

MarkMLl
Title: Re: alsa beep - linux only
Post by: winni on October 29, 2020, 01:51:33 am

I added ALSAbeeperStereo(), a stereo beeper with custom frequency for each channel.

https://github.com/fredvs/alsa_sound

Fre;D

Hi Fred!

I am still fiddeling with  the alsa_beep unit.

One small problem is:
When you play two differet tones anfter another, then every time the lib is loaded and unloaded.
That is nonsense. Anf results in a small gap between the two sounds.
This solves it:

Code: Pascal  [Select][+][-]
  1. initialization
  2. ab_Load;
  3. finalization
  4. ab_Unload;
  5. end.
  6.  

Second: sinus sound is not very interesting.
This can be easy changed by some lines of code inside the beep.

Here  are sinus as used before,  sqare waves and saw tooth waves:

Code: Pascal  [Select][+][-]
  1. for I := 0 to 359 do SA[I] := round(sin(pi * I / 180.0) * volume);           // create sine wave pattern
  2.  
Code: Pascal  [Select][+][-]
  1. for I := 0 to 359 do if I < 180 then SA[i] := +1*volume else  SA[i] := -1* volume;//  sqare wave

Code: Pascal  [Select][+][-]
  1. for I := 0 to 359 do SA[i] := (round((360 - i)/180) -1)*volume;   //   saw tooth wave  
   

The square wave posseses only the odd overtones.
It sounds like wooden wind instruments or like  a wooden pipe of an organ - top closed.

The saw tooth wave contains all overtones.
It sounds like a Bach trumpet or an metal organ pipe - top open.

No further knowledge of Fourier needed .....

And third I created a police horn - american style:

Code: Pascal  [Select][+][-]
  1. function ALSAPolice(BaseFreq,duration, volume: integer; speed: single): Boolean;
  2. var
  3.   buffer: array[0..9600 - 1] of byte;
  4.   frames: snd_pcm_sframes_t;    
  5.   pcm: PPsnd_pcm_t;
  6.   I, FC: integer;
  7.   SA: array[0..359] of shortint;
  8. const
  9.   device = 'default' + #0;                      // name of sound device
  10. var
  11.   count1, count2, N, X: integer;
  12.   DeltaStep: single;   //winni
  13.   delta : Integer;     //  "
  14.   PeakFreq: Integer;   // "
  15.   upDown  : Integer;   // "
  16. begin
  17.   Result := False;
  18.  
  19.   //ab_Load;
  20.  
  21.   if snd_pcm_open(@pcm, @device[1], SND_PCM_STREAM_PLAYBACK, 0) = 0 then
  22.     if snd_pcm_set_params(pcm, SND_PCM_FORMAT_U8,
  23.       SND_PCM_ACCESS_RW_INTERLEAVED,
  24.       1,                        // number of channels
  25.       48000,                    // sample rate (Hz)
  26.       1,                        // resampling on/off
  27.       500000) = 0 then          // latency (us)
  28.     begin
  29.       Result := True;
  30.       PeakFreq := round (BaseFreq * 4/3); //fourth - most used in signal horns
  31.       speed := EnsureRange (abs(speed),0.1,1.0); // avoid div by zero
  32.       speed := 1/speed *2400;
  33.       duration := EnsureRange(abs(duration),50,maxint);// 24.85 days
  34.       volume   := EnsureRange(abs(Volume),0,100);
  35.       // 48 samples per ms -->
  36.       // 360 / 48 = 7.5
  37.       upDown := 400; // ms interval
  38.       DeltaStep := 7.5*(PeakFreq - BaseFreq) /upDown;
  39.  
  40.       for I := 0 to 359 do
  41.         SA[I] := round(sin(pi * I / 180.0) * volume);           // create sine wave pattern
  42.       X       := 0;
  43.       N       := 0;                                                              // up/down counter used by unequal interval division
  44.  
  45.       count1 := 0;                                                         // count1 counts up, count2 counts down
  46.       count2 := duration * 48;                                               // (at 48000Hz there are 48 samples per ms)
  47.  
  48.       while count2 > 0 do                                                  // start making sound!
  49.       begin
  50.         FC    := 0;
  51.         for I := 0 to sizeof(buffer) - 1 do                                  // fill buffer with samples
  52.         begin
  53.           if count2 > 0 then
  54.           begin
  55.             if count1 < 480 then
  56.               buffer[I] := 128 + ((count1 * SA[X]) div 480)
  57.             else   // 10ms feather in
  58.             if count2 < 480 then
  59.               buffer[I] := 128 + ((count2 * SA[X]) div 480)
  60.             else   // 10ms feather out
  61.               buffer[I] := 128 + SA[X];
  62.             Inc(FC);
  63.           end
  64.           else
  65.           begin
  66.             buffer[I] := 128;                             // no signal on trailing end of buffer, just in case
  67.             if (FC mod 2400) <> 0 then
  68.               Inc(FC);            // keep increasing FC until is a multiple of 2400
  69.           end;
  70.  
  71.          delta := round (sin(Count1/speed)*DeltaStep*upDown*48/2);   // winni
  72.          Inc(N,BaseFreq*360+Delta);          // winni
  73.           while (N > 0) do
  74.           begin                                           // (a variation on Bresenham's Algorithm)
  75.             Dec(N, 48000);
  76.             Inc(X);
  77.           end;
  78.           X := X mod 360;
  79.  
  80.           Inc(count1);
  81.           Dec(count2);
  82.         end;
  83.  
  84.         frames   := snd_pcm_writei(pcm, @buffer, max(2400, FC));             // write AT LEAST one full period
  85.         if frames < 0 then
  86.           frames := snd_pcm_recover(pcm, frames, 0);        // try to recover from any error
  87.         if frames < 0 then
  88.           break;                                            // give up if failed to recover
  89.       end;
  90.       snd_pcm_drain(pcm);                                   // drain any remaining samples
  91.       snd_pcm_close(pcm);
  92.     end;
  93.   //ab_unload;
  94. end; //AlsaPolice
  95.  
  96.  

The top frequency is - like in most horns - the (musical) fourth of the BaseFreq -
relation 4 : 3.
BaseFreq should be something between 600 and 800 Hz.
Speed should be between zero and 1.0
Zero is shifted to 0.1 . If you need zero then you can use AlsaBeep ....

Winni

PS.:  The square waves were the first step to the synthesizer because the could easily be made with a flipflop. It was my first "sythesizer" with two BC 107 transistors when I was 14 .



Title: Re: alsa beep - linux only
Post by: MarkMLl on October 29, 2020, 09:40:34 am
PS.:  The square waves were the first step to the synthesizer because the could easily be made with a flipflop. It was my first "sythesizer" with two BC 107 transistors when I was 14 .

I'll forgive you a lot for that :-)

Have you considered a port of http://bulba.untergrund.net/Ay_Emul29b26.src.7z ? It's already based on an older version of the same libraries that Robert tidied up.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 29, 2020, 02:20:16 pm
https://github.com/fredvs/alsa_sound

One small problem is:
When you play two differet tones anfter another, then every time the lib is loaded and unloaded.
That is nonsense. Anf results in a small gap between the two sounds.
This solves it:

Code: Pascal  [Select][+][-]
  1. initialization
  2. ab_Load;
  3. finalization
  4. ab_Unload;
  5. end.
  6.  

Hum, did you see that there is the "CloseLib" parameter for each method?

Code: Pascal  [Select][+][-]
  1. function ALSAglide(StartFreq, EndFreq, Duration, Volume: integer;  CloseLib : boolean): Boolean;
  2.  
  3. function ALSAbeep(Frequency, Duration, Volume: integer; Warble: Boolean;   CloseLib: boolean): Boolean;
  4.  

The parameter CloseLib decides if the library must be closed after running the method.

In the example "alsa_sound_test.pas" it shows how to use multi-play:

Code: Pascal  [Select][+][-]
  1. program alsa_sound_test;
  2.  
  3. uses
  4. alsa_sound;
  5.  
  6. begin
  7. ALSAglide(20, 880, 500, 75, false);
  8. ALSAglide(880, 20, 500, 75, false);
  9. ALSAbeep(880, 100, 75, false, false);  
  10. ALSAbeep(840, 100, 75, false, false);
  11. ALSAbeepStereo(440, 660, 1000, 75, 50, false, true); // CloseLib = true to close the library
  12. end.

If you do it in initialization the library will be loaded even if no beep or few beeps are called:

Code: Pascal  [Select][+][-]
  1. initialization
  2. as_Load;
  3.  

But for finalization, it is already in code in case of people forgot to unload the library:

Code: Pascal  [Select][+][-]
  1. finalization
  2. as_Unload;
  3. end.
  4.  


[EDIT]
as_Load uses a reference counter so there is no problem to use it at beginning of each ALSAxxx() method.

and at end simply use this: 
Code: Pascal  [Select][+][-]
  1. if CloseLib then as_unload;
Title: Re: alsa beep - linux only
Post by: Fred vS on October 29, 2020, 06:20:32 pm
Hello Wini.

Quote
Second: sinus sound is not very interesting.
This can be easy changed by some lines of code

This is very interesting, you give all the keys for total control, many thanks.

Quote
And third I created a police horn - american style:

EXCELLENT and very WOW and easy to use.

Do you agree if I add into alsa_sound.pas + in the demo?
https://github.com/fredvs/alsa_sound

Fre;D
Title: Re: alsa beep - linux only
Post by: winni on October 30, 2020, 11:15:28 pm

Quote
And third I created a police horn - american style:

EXCELLENT and very WOW and easy to use.

Do you agree if I add into alsa_sound.pas + in the demo?
https://github.com/fredvs/alsa_sound

Fre;D

Yes, put it in the Alsa_beep unit and the demo!

Winni
Title: Re: alsa beep - linux only
Post by: Fred vS on October 30, 2020, 11:35:56 pm

Quote
And third I created a police horn - american style:

EXCELLENT and very WOW and easy to use.

Do you agree if I add into alsa_sound.pas + in the demo?
https://github.com/fredvs/alsa_sound

Fre;D

Yes, put it in the Alsa_beep unit and the demo!

Winni

Done, many thanks.
https://github.com/fredvs/alsa_sound/commit/e6beb42

About
Quote
Here  are sinus as used before,  sqare waves and saw tooth waves:

Do you agree if I use your code for the 'sinetype' parameter for some ALSAxxx methods?

Fre;D
Title: Re: alsa beep - linux only
Post by: winni on October 31, 2020, 01:34:28 am
Hi Fred!

Yes use the wave type stuff in the unit. But call it "WaveType" - that is more correct than "SineType".

And i have some more fun for the demo:

German alarm horns go always with the musical fourth , in this case
a1 and d2.

Code: Pascal  [Select][+][-]
  1. // Germany: ambulace, fire brigade, police
  2. // 440 Hz und 585 Hz: a1 - d2
  3. for i := 1 to 3 do
  4. begin
  5. ALSAbeep(440,400,20,false);
  6. AlsaBeep(585,400,20,false);
  7. end;                    

And the children sing with it: Too late, too late, .... ("Zu spät, zu spät, ...")

And Switzerland is always a little bit different.
Nice music by the horn of their mountain buses:

Code: Pascal  [Select][+][-]
  1. // swiss mountain bus
  2. // cis'–e–a :   277.183  164.814  220.000
  3. for i := 1 to 3 do
  4. begin
  5. ALSAbeep(277,400,20,false);
  6. AlsaBeep(165, 400,20,false);
  7. AlsaBeep(220, 400,20,false);
  8. sleep(200);
  9. end;        
  10.  

And Switzerland has a lot of mountains!

Put it in the demo if you like it.

Winni

PS.: Volume at 75%????
        Then my neighbors would be angry!
Title: Re: alsa beep - linux only
Post by: winni on October 31, 2020, 02:16:02 am

Have you considered a port of http://bulba.untergrund.net/Ay_Emul29b26.src.7z ? It's already based on an older version of the same libraries that Robert tidied up.

MarkMLl

Thanx for the the link.

What is that a strange kind of source?
A sound chip emu, a Motorola 680x0 sound emu, some hooks for the BASS.dll and in the last corner some code for Alsa.

Need some time to find out.

Winni

Title: Re: alsa beep - linux only
Post by: MarkMLl on October 31, 2020, 08:59:33 am
Have you considered a port of http://bulba.untergrund.net/Ay_Emul29b26.src.7z ? It's already based on an older version of the same libraries that Robert tidied up.
What is that a strange kind of source?

It was a very popular sound chip back in the day, my recollection is that it's got 3x tones plus noise with overall ASDR. I've seen a homebrew with I think three of them, and the results were extremely impressive. For anybody into murdering Mozart, see https://www.instructables.com/Arduino-MIDI-Chiptune-Synthesizer/

That project appears to have quite a lot of other stuff in there, possibly including Z80 and 68k emulators. The sound stuff is based on an earlier version of fpalsa, which is the library whence Robert and Fred cleaned up the PCM unit.

I'd mistakenly confused fpalsa and alsapas. It's now obvious that fpalsa is "lean and mean" while alsapas is anything but... why anybody should want to muddle a Lisp implementation in with the low-level libraries like this defies speculation.

Later: I notice that alsalisp (a derivative of slisp) is embedded in the C libraries themselves, so I am perhaps being a little uncharitable blaming alsapas for interfacing to it. Alsapas is creditably complete, but it's still a PITA to use since there's a runtime check that the expected version of libasound.so is on the system... forget about having a single development system targetting e.g. both Debian "stable" and "oldstable".

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on October 31, 2020, 12:03:48 pm
@Winni:

Your code was added in ALSA_Sound:
https://github.com/fredvs/alsa_sound/commit/c723f909

Many thanks!

Fre;D
Title: Re: alsa beep - linux only
Post by: winni on November 02, 2020, 11:33:30 pm
Hi Fred!

I did some " cleaning" at theunit alsa_sound:

* Moved a lot of stuff from the interface to the implementation

* Made a procedure SetWaveType to reduce code. The filling of the array[0..359] of shortint appears nearly in every beep-procedur so we need the code only once.

* I have added AlsaBeepFlanger and AlsaMexicanTruck - last is "La Cucaraca"  which they use as horn.

And finally I made a little App where the user kann play around with the sound:

3 trackbars for frequency, volume and duration. A Radiogroup for wavetype sinus/square/saw tooth. And so on.

Screenshot + source in attachment.

Winni
Title: Re: alsa beep - linux only
Post by: Fred vS on November 03, 2020, 02:43:47 am
Hello Winni.

Superb.

I did merge your cleaned code.
https://github.com/fredvs/alsa_sound/commit/68492aab

There is a little change in ALSAmexicantruck to turn it into a function and adapt the result for it.
Same for all others call to functions.

Lot of sounds now, we can do a international symphony!

Your demo is great too (but your license is GPL, alsa_sound is LGPL).

Many thanks.

Fre;D
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 03, 2020, 08:57:17 am
I continue to tinker with this at the lower levels, since now that I realise that some of my earlier problems (OS-version specific etc.) were due to alsapas I'm keen to be able to move over to MIDI via a shared object interface.

Since Fred's raised the (L)GPL, one comment if I may. The bulk of the fpalsa stuff is purely declarative, with the exception of a few inline functions intended to replace C macros. I note that Google et al. are claiming that interface declarations aren't subject to copyright since they are statements of fact rather than "literary works", and that most of the industry- with the obvious exception of Oracle- appears to have fallen in line behind them.

Obviously where I've transcluded headers which have a preexisting LGPL header I'm leaving it intact. But I suspect that a license on material that can't be copyrighted is ineffective, and am a little concerned lest the current habit of sticking (L)GPL boilerplate into absolutely every file is potentially strengthening Oracle's case.

My preferred practice, where a language supports separate declaration and implementation files, is to explicitly put the declaration part in the public domain and to explicitly put an (L)GPL at the start of the implementation part. And also to avoid mixing macros into the declaration part.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on November 03, 2020, 12:35:18 pm
Hello everybody.

I have a question about license.

What about using C header translated into Pascal?

For example here in alsa_sound project very few part of code is from fpalsa.

But, using the translator h2pas to translate alsa.h I get the same unit and can use it.

So there is something that trouble me, why should I follow the license of fpalsa if h2pas does perfectly the job?

Thanks.

Fre;D
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 03, 2020, 01:47:17 pm
I am not a lawyer, but from watching this stuff I'd say that the named entry points and the parameter list are statements of fact so cannot be copyrighted (hence possibly cannot have a license meaningfully associated with them), while parameter names and underlying type names might be explanatory so can be copyrighted/licensed. Comments and overall layout can most definitely be copyrighted, indexed entry points as used by DLLs probably have less protection than named ones.

Hence the output of h2pas definitely cannot have a stronger license than the input, and particularly if parameters and types are renamed are probably "res nullius" i.e. owned by nobody in particular as distinct from being owned by somebody but put in the public domain for free use.

Macros in a C/C++ header file are another can of worms entirely.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on November 03, 2020, 02:10:14 pm
Thanks MarkMLI for your light.

I have indeed some problems with the use of part of headers and the Indisputable Truth.

For example if I use some methods from a translated C header that use Static-loading of libraries  (external 'thelib') and change it into Dynamic-loading (loadlibrary('thelib') what is the status?

Because I changed the source, what about the license, is it a violation, must I notify somebody?

Fre;D

Title: Re: alsa beep - linux only
Post by: MarkMLl on November 03, 2020, 05:35:03 pm
Again with the caveat that I am not a lawyer, and suspecting that the Oracle vs Google case demonstrates that lawyers haven't really got to grips with this stuff.

For example if I use some methods from a translated C header that use Static-loading of libraries  (external 'thelib') and change it into Dynamic-loading (loadlibrary('thelib') what is the status?

I'd have thought that the overriding concern is the license under which you have been provided with the library- whether it be statically or dynamically linked- plus local case history on reverse engineering and clean-room regeneration via a specification.

If you have a declaration (e.g. Pascal interface section) which includes material which is recognisably a transcript of something subject to copyright and have not been licensed to use it, then you are at fault irrespective of the languages involved.

If you have reverse-engineered an interface in line with local convention (statute and precedent) which describes DLL-style numbered entry points, or a jump table at an absolute location in memory, and this contains absolutely no recognisable text, then I cannot see how you can be faulted.

If you have transcribed an interface which described named entry points, then you would possibly be liable for your use of identifiers such as open() or snd_pcm_open(). A court might or might not accept a defence of "it's obvious" or "it's the only way of doing it": those would invalidate a patent, but not necessarily copyright.

A court would probably apply the same reasoning to a data structure with named fields, however unreasonable this might appear to somebody "skilled in the art".

The libasound binary is undoubtedly subject to copyright, and to the copyright holders' chosen license. The .h files that accompany it are annotated as being subject to the LGPL. Whether a (Pascal) transcript of the .h files is also subject to the LGPL probably depends (a) on whether comments etc. have been carried over and (b) on whether some arcane interpretation of the (L)GPL allows a header file to be read with the intention of generating a clean-room specification.

I'm not sure that makes things any clearer, but it's the situation as I see it.

Quote
Because I changed the source, what about the license, is it a violation, must I notify somebody?

If somebody had asserted copyright over a file which you changed and supplied it subject to a license which says you must tell them, then yes.

I speculate that you don't have to tell anybody if you generate a cleanroom copy, subject to the (L)GPL being interpreted to allow this or your demonstration that you've done it from a binary without reference to the source.

It's a mess. It's far easier- at least with scratch-written software- to explicitly designate interface files as a statement of fact hence not being copyrightable, with the caveat that if local laws prohibit this that they're in the public domain. At the very least, that avoids risk that the (L)GPL be weakened by a challenge.

Mixing declaration and implementation, including C/C++ macros, in a single file is asking for trouble.

MarkMLl
Title: Re: alsa beep - linux only
Post by: winni on November 03, 2020, 10:48:38 pm
Hi!

Donald Trump breaks laws and does not respect the Constitution.
The same with Boris Johnson and Angela Merkel.
Not to talk about all those dictators which surround us in Turkey,
Russia or Hungary and Poland.

So why should I worry about license stuff?

Winni
 
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 03, 2020, 11:14:03 pm
Because you're better than they are and you know it.

MarkMLl
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 06, 2020, 01:43:45 pm
Here's my take on a partial ALSA interface based on fpalsa etc. (i.e. as is currently being discussed).

It includes support for PCM and sequencer devices, but only to the extent that this is necessary for the sort of PCM demo we've been kicking around and the sort of MIDI stuff I'm using for annunciator jingles. I've folded in as many comments as I can from the main ALSA documentation site so that they show up in the IDE.

It includes both static- and dynamically-linked variants, both test OK. The dynamic linkage stuff relies on a hack somebody gave me about 20 years ago, and application code can be written to use either subject to (a) a change in the "uses" clause and (b) referring explicitly to AsoundPcm or AsoundSeq wherever routines are used (i.e. provided that (b) is observed then only (a) needs attention).

There's quite a lot of boilerplate involved particularly in the dynamic units, but it's done in such a way that there is a definitive .inc file for each subsystem. If I did this sort of thing often enough, I'd consider a generator program to augment h2pas.

This has allowed me to dispense with alsapas, which had an extremely irritating dependency on the version of the asound library on the target system which meant that I was having to build multiple copies of each application.

I'll stick it onto https://github.com/MarkMLl presently but would appreciate any feedback.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on November 06, 2020, 07:14:45 pm
Hello MarkMLI

I did try your /examples/BeepDemo.lpi but without luck:

There is a error message: 'Main source code is missing".
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 06, 2020, 07:57:59 pm
I've just downloaded the file I posted. Untar, switch to the examples directory, start Lazarus on BeepDemo.lpi. Compiled and ran fine.

No demo for the MIDI side I'm afraid, most of the complexity is in enumerating the available devices.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on November 06, 2020, 10:50:27 pm
I've just downloaded the file I posted. Untar, switch to the examples directory, start Lazarus on BeepDemo.lpi. Compiled and ran fine.

No demo for the MIDI side I'm afraid, most of the complexity is in enumerating the available devices.

MarkMLl

Hello

I just re-downloaded your file, un-tar it, load the project with Lazarus and try to compile it but I get this (see picture):
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 06, 2020, 11:09:31 pm
I just re-downloaded your file, un-tar it, load the project with Lazarus and try to compile it but I get this (see picture):

Using Lazarus trunk 2.1.0 r63879 with FPC 3.0.4 on Debian 10 "stable". No extra packages and nothing special about the compiler.

Only requirement is to use the supplied .lpi in the examples directory, with the support units (both static and dynamic) in ../

Try starting off with the .lpr instead, in case Lazarus has broken backward compatibility.

MarkMLl
Title: Re: alsa beep - linux only
Post by: Fred vS on November 06, 2020, 11:11:07 pm
Hello.

I was able to compile your demo using a other IDE.

On my system, only button 'ALSA PCM Beep' produces sound, the 2 others only silence.
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 07, 2020, 09:48:24 am
On my system, only button 'ALSA PCM Beep' produces sound, the 2 others only silence.

As expected. You'd probably find that if you ran the program from a shell and echo -e \a worked then the stdout button would also work. The erratic X11 beep we've done to death in the past.

I remembered overnight that a change in the IDE broke file compatibility, but that there's a project-level setting to write compatible files.

MarkMLl


Title: Re: alsa beep - linux only
Post by: Fred vS on November 07, 2020, 03:01:45 pm
On my system, only button 'ALSA PCM Beep' produces sound, the 2 others only silence.

As expected. You'd probably find that if you ran the program from a shell and echo -e \a worked then the stdout button would also work. The erratic X11 beep we've done to death in the past.

I remembered overnight that a change in the IDE broke file compatibility, but that there's a project-level setting to write compatible files.

MarkMLl

Hello MarkMLI.

This gives silent on my system (as already explained in other posts).

Code: Bash  [Select][+][-]
  1. fred@fiens ~> echo -e '\a'

So, (also already explained) better to forget it if you want something universal.

But we are all very interested about your MIDI code + example.

Many thanks.

Fre;D

Title: Re: alsa beep - linux only
Post by: MarkMLl on November 07, 2020, 03:38:31 pm
OK, I'll try to work on a MIDI example to go with it (definitely won't be today). But it might need manual entry of the device selection numbers.

MarkMLl
Title: Re: alsa beep - linux only
Post by: MarkMLl on November 11, 2020, 07:44:16 pm
Here's an extension of the example to also handle MIDI output. I warn you that it's idiosyncratic, it's basically something that I wrote 20 years ago on Windows which has been hacked about recently to use ALSA on Linux.

I'm not saying it's the best way to do it, I'm not even saying it's a right way to do it (i.e. I'm sure that ALSA MIDI experts would shoot me down). Most of the complexity is in the code that looks at available devices, and works out which of them are useful soundcards etc.

However, I think that the significant thing in my example is the way that I've recycled somebody else's idea to wrap the DLL/so module. I think the only bit I'd claim to be my own is the way that I've organised things so that "AsoundSeq" etc. can be either a unit or an object, which appear identical from the POV of application code.

This stuff gets far more interesting when you write your own dynamically-loadable module (e.g. some sort of plugin), and export entry points from the main program for it to use. However the last time I tried that it was problematic... I don't know if that was because I'd got 64-bit code rather than the 32-bit code I'd used in the past, or if there was something in there specific to a given version of FPC code generation.

MarkMLl


TinyPortal © 2005-2018