Review of extensions for Vicare (part 1)

Posted on May 26, 2015

I am doing a review of Vicare’s extensions implementing interfaces to foreign libraries. Having formally abandoned Sourceforge and Gitorious, the code is on Github and Bitbucket. Yes, I am procrastinating the expander’s code review I have started.

Vicare/CRE2

For some time Vicare’s package has included an interface to the C language library CRE2, which in turn is a C wrapper for the C++ language library re2. re2 was originally hosted at Google Code, but it is now hosted at Github.

The purpose of including this interface in the main Vicare Scheme package was twofold: to include a fast regular expressions engine; to test the viability of inclusion of foreign library interfaces in the main package:

So, I have extracted the interface to CRE2 from the main package and it has now its own package. There is nothing new about it; the api has not changed.

Vicare/AO

This is a new package interfacing Libao: a cross–platform audio library that allows programs to output audio using a simple api on a wide variety of platforms (this description is straight from their web site). I doubt it can be used for serious stuff, but, anyway, it can be used to play sounds programmatically generated by direct computation of the waveform; it can also generate wav files.

Here is an example playing a chunk of Black Sabbath’s “Iron Man” song:

(import (vicare)
  (prefix (vicare multimedia ao) ao.)
  (prefix (vicare multimedia ao constants) ao.)
  (vicare numerics constants))

(ao.ao-initialise)

(define-constant A4   440.00)
(define-constant B4   493.88)
(define-constant C5   523.25)
(define-constant D5   587.33)
(define-constant E5   659.25)
(define-constant F5   698.46)
(define-constant Fs5  739.995)
(define-constant G5   783.99)
(define-constant A5   880.00)
(define-constant B5   987.77)
(define-constant C6  1046.50)

(define sample-format
  (let ((bits         16)    ;how many bits per sample
        (rate         44100) ;how many samples per second
        (channels     2)
        (byte-format  ao.AO_FMT_LITTLE)
        (matrix       "L,R"))
    (ao.make-ao-sample-format bits rate channels byte-format matrix)))

(define device
  (let ((driver-options #f))
    (ao.ao-open-live (ao.ao-default-driver-id) sample-format driver-options)))

(define (make-melody frm melody)
  ;;Build and return a samples bytevector, filled with a sinusoidal waveform.
  ;;
  (define number-of-ticks-per-second
    (infix inexact(ao.ao-sample-format-rate(frm))))
  (define number-of-ticks-per-millisecond
    (infix number-of-ticks-per-second / 1000.0))
  (define ticks*
    (map (lambda (note)
           (let ((msecs (cdr note)))
             (infix exact(floor(msecs * number-of-ticks-per-millisecond)))))
      melody))
  (define samples.len
    (let ((total-number-of-ticks (fold-left + 0 ticks*)))
      (infix ao.ao-sample-format-bits(frm) / 8
             * ao.ao-sample-format-channels(frm)
             * total-number-of-ticks)))
  (receive-and-return (samples.bv)
      (make-bytevector samples.len)
    (fold-left
        (lambda (i.start note ticks)
          (let ((freq  (car note))
                (msecs (cdr note)))
            (receive-and-return (i.end)
                (+ i.start ticks)
              (fill-tone! samples.bv i.start i.end freq number-of-ticks-per-second))))
      0 melody ticks*)))

(define (fill-tone! samples.bv i.start i.end freq number-of-ticks-per-second)
  ;;FREQ must be the tone frequency in Hertz.
  ;;
  (define A     (infix 0.75 * 32768.0))
  (define omega (infix 2 * greek-pi * freq))
  (define sample-max (exact (floor (* A 0.20))))
  (define sample-min (- sample-max))
  (do ((i i.start (fxadd1 i)))
      ((fx>=? i i.end))
    (let* ((time   (infix inexact(i / number-of-ticks-per-second)))
           (sample (exact (floor (infix A * sin(omega * time)))))
           (j      (infix 4 * i)))
      ;;Put the same stuff in left and  right channels.  The bytevector is an array
      ;;of 32-bit slots, each with format:
      ;;
      ;;    channel 1 LSB channel 2 LSB channel 1 MSB channel 2 MSB
      ;;   |-------------|-------------|-------------|-------------|
      ;;
      ;;where  LSB stands  for  Least  Significant Byte  and  MSB  stands for  Most
      ;;Significant Byte.
      ;;
      (let ((sample.lsb (infix #xFF & sample)))
        (bytevector-u8-set! samples.bv j       sample.lsb)
        (bytevector-u8-set! samples.bv (+ j 2) sample.lsb))
      (let ((sample.msb (infix #xFF & (sample >> 8))))
        (bytevector-u8-set! samples.bv (+ j 1) sample.msb)
        (bytevector-u8-set! samples.bv (+ j 3) sample.msb)))))

(define iron-man
  (let ((T 1500)
        (pause '(0 . 100))
        (pause/2 '(0 . 50)))
    (make-melody sample-format
                 `((,B4  . ,(* T 1/4)) ,pause
                   (,D5  . ,(* T 1/4)) ,pause
                   (,D5  . ,(* T 1/8)) ,pause
                   (,E5  . ,(* T 1/8)) ,pause
                   (,E5  . ,(* T 1/4)) ,pause
                   (,G5  . ,(* T 1/16)) ,pause/2
                   (,Fs5 . ,(* T 1/16)) ,pause/2
                   (,G5  . ,(* T 1/16)) ,pause/2
                   (,Fs5 . ,(* T 1/16)) ,pause/2
                   (,G5  . ,(* T 1/16)) ,pause/2
                   (,Fs5 . ,(* T 1/16)) ,pause/2
                   (,D5  . ,(* T 1/8)) ,pause
                   (,D5  . ,(* T 1/8)) ,pause
                   (,E5  . ,(* T 1/8)) ,pause
                   (,E5  . ,(* T 1/4))
                   ))))

(ao.ao-play device iron-man)
(ao.ao-close device)

Libao’s documentation is very dry. In future I should dig in the code and document what is the expected format of audio samples, it is not really clear to me.

Vicare/SQLite

I have revamped the interface to SQLite, the database engine. The master branch has a tag begin-support-for-vicare-scheme-0.4 after which Vicare Scheme version 0.4 must be used. I did a review of the code. The current head of Vicare/SQLite’s master branch should work with the current head of Vicare Scheme’s master branch.

On dependencies between libraries

The current scenario is that: there is a tight dependence between the boot image and compiled libraries. First we have to compile the boot image, then we have to hierarchically compile the libraries. If we recompile a library: we have to recompile all the libraries that depend on it. If we recompile the boot image: we have to recompile everything. I hate this.

This was less a problem before, when the scenario was: we install source libraries and automatically compile them in the fasl files cache.

I want to do something about it, so that the scenario is more like the shared objects’s hell of C language libraries. “Doing something” means to implement tables of storage location gensyms referencing exported syntactic bindings.