tribusonz-2 / rb_wave

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

How about the digitizer implementation using Ruby C API #2

Closed tribusonz-2 closed 3 months ago

tribusonz-2 commented 3 months ago

I had an implementation plan to use Ruby's pack/unpack C API to handle digitizers.
in C level, as follows:

// for Encode
static VALUE
test_intle2bytes(VALUE unused_obj, VALUE num, VALUE bytesize)
{
    static char s[4];
    size_t sz = NUM2SIZET(bytesize);

    if (TYPE(num) != T_FIXNUM)
        num = rb_to_int(num);

    if (sz > 4)  sz = 4;
    rb_integer_pack(num, s, sz, 8, 1, 
    INTEGER_PACK_LITTLE_ENDIAN | INTEGER_PACK_2COMP);

    return rb_str_new(s, (long)sz);
}

// for Decode, to signed value
static VALUE
test_bytes2intle(VALUE unused_obj, VALUE buf)
{
    char *s = StringValuePtr(buf);

    return rb_integer_unpack(s, RSTRING_LEN(buf), 1, 0, 
    INTEGER_PACK_LITTLE_ENDIAN | INTEGER_PACK_2COMP);
}

It has very good processing speed.
However, in practice, it seems faster to combine the digitizer and normalizer.

The decoding, as follows:

void
pcm_read_8bit(unsigned char buf[], double s[])
{
    char data = (buf[0] - 128);
    *s = data / (double)0x80;
}

void
pcm_read_16bit(unsigned char buf[], double s[])
{
    int16_t data = (int16_t)(buf[0] | buf[1] << 8);
    *s = data / (double)0x8000;
}

void
pcm_read_24bit(unsigned char buf[], double s[])
{
     int data = (buf[0] | buf[1] << 8 | buf[2] << 16);
     if (data & 0x800000)  data -= 0x1000000;
    *s = data / (double)0x800000;
}

void
pcm_read_32bit(unsigned char buf[], double s[])
{
    int32_t data = (int32_t)(buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24);
    *s = data / (double)0x80000000;
}

In encoding, the normalizer requires guarantees that the values ​​are reliably digitized.
This requires the skills of a programmer.

My implementation example is below.
First, normalize to ensure digitization, cast to an integer, and write this.

static inline double
fclip(double x, double min, double max)
{
    return fmin(fmax(min, x), max);
}

static inline double
wave_normalize(double x, double min, double max, double rate)
{
    if (x != x)  x = 0;
    return fclip(x * rate, min, max);
}

void
pcm_write_8bit(unsigned char buf[], double s[])
{
    double digitize = wave_normalize(s[0], INT8_MIN, INT8_MAX, 0x80);

    *buf = (unsigned char)(digitize + 0x80);
}

void
pcm_write_16bit(unsigned char buf[], double s[])
{
    double digitize = wave_normalize(s[0], INT16_MIN, INT16_MAX, 0x8000);
    int16_t bytes = (int16_t)digitize;

    buf[0] = bytes & 0xFF;
    buf[1] = (bytes >> 8) & 0xFF;
}

void
pcm_write_24bit(unsigned char buf[], double s[])
{
    double digitize = wave_normalize(s[0], -0x800000, 0x7FFFFF, 0x800000);
    int32_t bytes = (int32_t)digitize;

    buf[0] = bytes & 0xFF;
    buf[1] = (bytes >> 8) & 0xFF;
    buf[2] = (bytes >> 16) & 0xFF;
}

void
pcm_write_32bit(unsigned char buf[], double s[])
{
    double digitize = wave_normalize(s[0], INT32_MIN, INT32_MAX, 0x80000000);
    int32_t bytes = (int32_t)digitize;

    buf[0] = bytes & 0xFF;
    buf[1] = (bytes >> 8) & 0xFF;
    buf[2] = (bytes >> 16) & 0xFF;
    buf[3] = (bytes >> 24) & 0xFF;
}