Recent

Author Topic: alsa beep - linux only  (Read 774 times)

robert rozee

  • New Member
  • *
  • Posts: 17
alsa beep - linux only
« 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   :-)


« Last Edit: April 24, 2020, 06:04:11 pm by robert rozee »

MarkMLl

  • Hero Member
  • *****
  • Posts: 925
Re: alsa beep - linux only
« Reply #1 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
« Last Edit: April 24, 2020, 06:44:08 pm by MarkMLl »
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

robert rozee

  • New Member
  • *
  • Posts: 17
Re: alsa beep - linux only
« Reply #2 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   :-)

MarkMLl

  • Hero Member
  • *****
  • Posts: 925
Re: alsa beep - linux only
« Reply #3 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
« Last Edit: April 25, 2020, 11:31:16 am by MarkMLl »
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

robert rozee

  • New Member
  • *
  • Posts: 17
Re: alsa beep - linux only
« Reply #4 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   :-)

MarkMLl

  • Hero Member
  • *****
  • Posts: 925
Re: alsa beep - linux only
« Reply #5 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
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

robert rozee

  • New Member
  • *
  • Posts: 17
Re: alsa beep - linux only
« Reply #6 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

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
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!
« Last Edit: April 26, 2020, 07:52:52 pm by robert rozee »

MarkMLl

  • Hero Member
  • *****
  • Posts: 925
Re: alsa beep - linux only
« Reply #7 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
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

Thaddy

  • Hero Member
  • *****
  • Posts: 10189
Re: alsa beep - linux only
« Reply #8 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
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)
« Last Edit: April 29, 2020, 10:58:31 am by Thaddy »
I am more like donkey than shrek

robert rozee

  • New Member
  • *
  • Posts: 17
Re: alsa beep - linux only
« Reply #9 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.
« Last Edit: May 22, 2020, 05:18:33 pm by robert rozee »

 

TinyPortal © 2005-2018