www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - avoiding loops, allocation using arrays and ranges

reply Danni Coy via Digitalmars-d <digitalmars-d puremagic.com> writes:
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.
Mar 28 2016
parent reply Basile B. <b2.temp gmx.com> writes:
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.
Mar 28 2016
parent reply Danni Coy via Digitalmars-d <digitalmars-d puremagic.com> writes:
     writeln(v.array); // everything gets eval by .array
.array allocates a new array. I want to store the result of v in a buffer that I have preallocated in my case this just happens to be a[]; This is quite important. This is also the step I would like a more elegant way to deal with.
 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.
Thank you I will certainly take a look at these.
Mar 28 2016
parent reply Basile B. <b2.temp gmx.com> writes:
On Monday, 28 March 2016 at 08:13:08 UTC, Danni Coy wrote:
     writeln(v.array); // everything gets eval by .array
.array allocates a new array. I want to store the result of v in a buffer that I have preallocated in my case this just happens to be a[]; This is quite important. This is also the step I would like a more elegant way to deal with.
.array is just used to render the result in this demo. It doesn't need to be used at all. Anyway the result can be yielded in a pre-allocated array without problem, at the end of the functional stuff just loop on the resulting range and copy. With UFCS this can do something like: foreach(s; a.process!(xfade)(b, 4).process!(xfade)(b, 4)) {/*copy each sample to pre alloc array*/}
Mar 28 2016
parent reply Mithun Hunsur <me philpax.me> writes:
On Monday, 28 March 2016 at 08:21:33 UTC, Basile B. wrote:
 On Monday, 28 March 2016 at 08:13:08 UTC, Danni Coy wrote:
     writeln(v.array); // everything gets eval by .array
.array allocates a new array. I want to store the result of v in a buffer that I have preallocated in my case this just happens to be a[]; This is quite important. This is also the step I would like a more elegant way to deal with.
.array is just used to render the result in this demo. It doesn't need to be used at all. Anyway the result can be yielded in a pre-allocated array without problem, at the end of the functional stuff just loop on the resulting range and copy. With UFCS this can do something like: foreach(s; a.process!(xfade)(b, 4).process!(xfade)(b, 4)) {/*copy each sample to pre alloc array*/}
I believe you're able to use https://dlang.org/phobos/std_algorithm_mutation.html#.copy to achieve this.
Mar 28 2016
parent Danni Coy via Digitalmars-d <digitalmars-d puremagic.com> writes:
 I believe you're able to use
 https://dlang.org/phobos/std_algorithm_mutation.html#.copy to achieve this.
yes that did the trick and alias copyTo = copy; event makes it unambiguous which direction the copy is going in :)
Mar 28 2016