tribusonz-2 / rb_wave

Wave library for Ruby
MIT License
0 stars 0 forks source link

Implementation of encoding/decoding using closure #3

Open tribusonz-2 opened 3 months ago

tribusonz-2 commented 3 months ago

When inputting and outputting audio files, data exchange using closures is most suitable.
I get a slightly meaningful error when writing with the #write method.
Passing PCM classes with different frequencies/sizes in an array throws a semantic error similar to the following:

"Exporting each channel's the different ### is not supported yet"

Yet... What "yet"?

SLAKE (Takehiko Fujii) told me "to create a sampler", so I created a routine like the one below using closures.

def audioclient(vector)
  enum = vector.enum_for(:each)
  ->{
    loop do
      return enum.next
    end
    return 0.0
  }
end

def main
  vector1 = Array.new(10){(rand-0.5)*2}
  vector2 = Array.new(16){(rand-0.5)*2}
  mixer = Array.new(2)
  mixer[0] = audioclient(vector1)
  mixer[1] = audioclient(vector2)

  20.times do |i|
    p mixer.map {|client| client.call}
  end
end

main
#=> [0.7064661083886434, 0.23767742860663676]
#=> [0.5140081469621989, 0.0036619062193694596]
#=> [0.7669447107229099, 0.02087963281521188]
#=> [0.5435803866259479, 0.25614368496310047]
#=> [-0.1809655254470417, -0.47441357158825026]
#=> [0.7287987755911116, 0.5043952533538847]
#=> [0.7199471888482307, 0.20250054163877262]
#=> [0.9682745621889539, 0.43646251680091575]
#=> [0.2392610947310838, -0.28959053164321746]
#=> [-0.8333190777778108, 0.2844694929932423]
#=> [0.0, -0.9408717058272795]
#=> [0.0, 0.9596422576110293]
#=> [0.0, -0.9616840221855663]
#=> [0.0, 0.08033563493342855]
#=> [0.0, 0.4387505915598844]
#=> [0.0, 0.38115140609540377]
#=> [0.0, 0.0]
#=> [0.0, 0.0]
#=> [0.0, 0.0]
#=> [0.0, 0.0]

Have you ever noticed that an array of audio data keeps creating 0.0 forever even when it reaches the end? Yes, just write this in C.

tribusonz-2 commented 3 months ago

This is an example implementation in C.

/* Lexical Environment */
typedef struct {
    long pos;
    long len;
} snd_env;

/* Queries the pointer position */
long 
ptr_idx(snd_env *env)
{
    if (env->pos >= env->len)  return -1; // Notify '-1' if the number of arrays more
    return env->pos++;
}

/* Closure */
typedef struct {
    snd_env env;;
    long (*lambda)(snd_env *);
} snd_closure;

/* Instance */
snd_closure snd_broker(long len)
{
    snd_closure c;
    c.env.pos = 0;
    c.env.len = len;
    c.lambda = ptr_idx;
    return c;
}

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int
main(void)
{
    size_t size = 16;
    snd_closure c1 = snd_broker((long)size);
    double *s = (double *)malloc(size * sizeof(double));
    for (size_t i = 0; i < size; i++)
    {
        s[i] = 0.1 * sin(2 * M_PI * 500.0 * i / 8000);
    }

    for (long i = 0; i < 20; i++)
    {
        const long index = c1.lambda(&c1.env);
        const double snd = index == -1 ? 0.0 : s[index];
        printf("%02ld % f\n", i, snd);
    }
    free(s);
}

/* Result
00  0.000000
01  0.038268
02  0.070711
03  0.092388
04  0.100000
05  0.092388
06  0.070711
07  0.038268
08  0.000000
09 -0.038268
10 -0.070711
11 -0.092388
12 -0.100000
13 -0.092388
14 -0.070711
15 -0.038268
16  0.000000
17  0.000000
18  0.000000
19  0.000000
*/
tribusonz-2 commented 3 months ago

I thought Mutex was not necessary. The functions of these closures will be aggregated into procedures. Since the procedure is for each chunk, a critical section will always occur. From the perspective of atomicity, it seems like any amount is necessary.

schedule(io) {
  threads << Thread(each_chunk) {
    procedure(methods) {
      Mutex(lock) { read/write }
    }
  }
}