avoiding loops, allocation using arrays and ranges

Basile B. via Digitalmars-d digitalmars-d at puremagic.com
Mon Mar 28 01:01:44 PDT 2016


On Monday, 28 March 2016 at 07:07:47 UTC, Danni Coy wrote:
> Something just clicked in my head in the last week in terms of 
> component based programming.
>
> I am currently porting a C++ realtime audio app to D.
> The app uses jackd.
> I have a series of float[] buffers I read and write audio data 
> from.
>
> lets say I want to crossfade to audio buffers I would do the 
> following
>
> float[1024] a; //actually these allocated by jackd in C but for
> purposes of illustration
> float[1024] b;
>
> length = min(length,a.length,b.length);
>
> foreach(i; 0..nframes)
> {
>    /* calculations */
>     a[i] = a[i]*ratio + b[i] * (1-ratio);
> }
>
> I can do :
>
> auto crossfade = sequence(/*calculations*/)(a[],b[],length);
> foreach(i; 0..length) a[i] = crossfade[i];
>
> but it would be nice if I could do :
>
> auto crossfade = sequence(/*calculations*/)(a[],b[],length);
> a[0..length] = crossfade[0..length];
>
> or even better
> auto ramp = sequence(/*calculations*/)(length); // create once 
> and
> store for later
> a[] = a[]*ramp[] + b[]*(1.0-ramp[]);
>
> I know I can use array but that allocates memory and I don't 
> want to
> do that in my realtime thread.
> I could store the ramp as a static array but I want to option 
> to do
> this lazily, particularly if I end up with a lot of different
> permutations.

I would see a complete lazy processing a bit like that:

auto process(alias fun, R0, R1)(R0 r0, R1 r1, size_t len)
if (isInputRange!R0 && isInputRange!R1 && is(ElementType!R0 == 
ElementType!R1))
{
     struct Processor
     {
         size_t i;
         bool empty()
         {
             return r0.empty || r1.empty;
         }
         void popFront()
         {
             ++i;
             r0.popFront;
             r1.popFront;
         }
         auto front()
         {
             return fun(i, len, r0.front, r1.front);
         }
     }
     Processor proc;
     return proc;
}

void main(string[] args)
{
     float[] a = [0.0,0.0,0.0,0.0];
     float[] b = [1.0,1.0,1.0,1.0];

     float xfade(size_t i, size_t len, float sa, float sb)
     {
         auto ramp = (1.0 / --len) * i;
         return sa * ramp + (1.0 - ramp) * sb;
     }

     auto r = process!(xfade)(a, b, 4); // not evaluated
     auto v = process!(xfade)(r, b, 4); // still not...

     writeln(v.array); // everything gets eval by .array
}

So that you can get rid of all operators in the processing loop.
This is only a demo, I think this could be more elegant and 
verstatile (for example the fact that the ramp could itself be a 
lazy range).

There's probably some interesting stuff that covers this in 
std.range. Look at the functions that take some "Range Of Ranges" 
(RoR) as parameter. like transpose, transversal, etc.


More information about the Digitalmars-d mailing list