diff --git a/source/VBAPUGens/VBAP.cpp b/source/VBAPUGens/VBAP.cpp index 67ecaf503..a3a0b4bd0 100644 --- a/source/VBAPUGens/VBAP.cpp +++ b/source/VBAPUGens/VBAP.cpp @@ -101,8 +101,8 @@ struct VBAP : Unit float* *x_set_matx; /* matrice for each loudspeaker set */ int* *x_lsset; /* channel numbers of loudspeakers in each LS set */ int x_lsset_available; /* have loudspeaker sets been defined with define_loudspeakers */ - int x_lsset_amount; /* amount of loudspeaker sets */ - int x_ls_amount; /* amount of loudspeakers */ + int x_num_lssets; /* number of loudspeaker sets (pairs or triplets) */ + int x_num_ls; /* number of loudspeakers */ int x_dimension; /* 2 or 3 */ float x_spread; /* speading amount of virtual source (0-100) */ float x_spread_base[3]; /* used to create uniform spreading */ @@ -260,7 +260,7 @@ static void additive_vbap(float *final_gs, float cartdir[3], VBAP *x) * bug. 2006-08-13 */ // post("x_lsset_amount: %li", x->x_lsset_amount); - for(i=0;ix_lsset_amount;i++){ + for(i=0;ix_num_lssets;i++){ small_g = 10000000.0; neg_g_am = 3; for(j=0;jx_spread > 70) - for(i=0;ix_ls_amount;i++){ + for(i=0;ix_num_ls;i++){ final_gs[i] += (x->x_spread - 70) / 30.0 * (x->x_spread - 70) / 30.0 * 10.0; } - for(i=0,power=0.0;ix_ls_amount;i++){ + for(i=0,power=0.0;ix_num_ls;i++){ power += final_gs[i] * final_gs[i]; } power = sqrt(power); - for(i=0;ix_ls_amount;i++){ + for(i=0;ix_num_ls;i++){ final_gs[i] /= power; } } @@ -430,7 +430,7 @@ static void vbap(float g[3], int ls[3], VBAP *x) best_neg_g_am=3; /* how many negative values in this set */ - for(i=0;ix_lsset_amount;i++){ + for(i=0;ix_num_lssets;i++){ small_g = 10000000.0; neg_g_am = 3; for(j=0;jx_ele = elevation; unit->x_spread = spread; - if(unit->x_lsset_available ==1){ - vbap(g,ls, unit); - for(i=0;ix_ls_amount;i++) - final_gs[i]=0.0; - for(i=0;ix_dimension;i++){ - final_gs[ls[i]-1]=g[i]; - } - if(unit->x_spread != 0){ - spread_it(unit,final_gs); - } -// for(i=0; i < unit->mNumOutputs; i++){ -// printf("chan %i: %f\n", i, final_gs[i] ); -// } - } + vbap(g,ls, unit); + for(i=0;ix_num_ls;i++) + final_gs[i]=0.0; + for(i=0;ix_dimension;i++){ + final_gs[ls[i]-1]=g[i]; + } + if(unit->x_spread != 0){ + spread_it(unit,final_gs); + } +// for(i=0; i < unit->mNumOutputs; i++){ +// printf("chan %i: %f\n", i, final_gs[i] ); +// } } } @@ -596,22 +594,16 @@ static inline_functions void VBAP_next_simd(VBAP *unit, int inNumSamples) } #endif -// needs to check that numOutputs and x_ls_amount match!! static void VBAP_Ctor(VBAP* unit) { //printf("VBAP-1.0.3.2\n"); - int numOutputs = unit->mNumOutputs, counter = 0, datapointer=0, setpointer=0, i; - - unit->m_chanamp = (float*)RTAlloc(unit->mWorld, numOutputs * sizeof(float)); - - // initialise interpolation levels and outputs - for (int i=0; im_chanamp[i] = 0; - ZOUT0(i) = 0.f; - } + int numOutputs = unit->mNumOutputs; + int datapointer=0, setpointer=0; + int i; + + /* Access loudspeaker data buffer, validate */ - // [dim, numSpeakers, [chanOffsets 0-2, invmx 0-8, [lp1, lp2, lp2].x, sim.y, sim.z] * sets.size].flat - float fbufnum = ZIN0(1); + float fbufnum = ZIN0(1); uint32 ibufnum = (uint32)fbufnum; World *world = unit->mWorld; @@ -628,56 +620,82 @@ static void VBAP_Ctor(VBAP* unit) buf = world->mSndBufs + ibufnum; } - int numvals = buf->samples; - unit->x_dimension = (int)(buf->data[datapointer++]); - unit->x_ls_amount = (int)(buf->data[datapointer++]); - - unit->x_azi = unit->x_ele = unit->x_spread = std::numeric_limits::quiet_NaN(); - - unit->final_gs = (float*)RTAlloc(unit->mWorld, numOutputs * sizeof(float)); - unit->x_lsset_available = 1; - - if(((unit->x_dimension != 2) && (unit->x_dimension != 3)) || (unit->x_ls_amount < 2)) { - printf("vbap: Error in loudspeaker data. Bufnum: %i\n", (int)fbufnum); - unit->x_lsset_available = 0; - // do something else here + // Validate data header: dimension and number of loudspeakers + bool passing = true; + int dim = (int)(buf->data[datapointer++]); // 2D or 3D + int num_ls = (int)(buf->data[datapointer++]); // number of loudspeakers + + if(numOutputs != num_ls) { + printf("VBAP Error: ugen's numOutputs (%d) does not match the number of loudspeakers reported in the loudspeaker buffer (%d). Bufnum: %i\n", numOutputs, num_ls, (int)fbufnum); + passing = false; + } + + if(((dim != 2) && (dim != 3)) || (num_ls < 2)) { + printf("VBAP Error: Error in loudspeaker data. Dimension must be 2 or 3 (%d specified), and there must be at least 2 loudspeakers (%d provided). Bufnum: %i\n", dim, num_ls, (int)fbufnum); + passing = false; } - if(unit->x_dimension == 3) - counter = (numvals - 2) / ((unit->x_dimension * unit->x_dimension*2) + unit->x_dimension); - if(unit->x_dimension == 2) - counter = (numvals - 2) / ((unit->x_dimension * unit->x_dimension) + unit->x_dimension); - unit->x_lsset_amount=counter; - - if(counter<=0){ - printf("vbap: Error in loudspeaker data. Bufnum: %i\n", (int)fbufnum); - unit->x_lsset_available=0; -// return; - } + // Validate loudspeaker set payload + // buffer data structure: + // 2D: [dim, numSpeakers, numSets * [pairChanIndices (1x2), invmtx (2x2), fwdmtx (2x2)]].flat + // 3D: [dim, numSpeakers, numSets * [tripChanIndices (1x3), invmtx (3x3), spkcoords (3x3)]].flat + // Size of this buffer factors to: 2 + (numSets * (dim + (dim * dim) + (dim * dim))) + // Confirm with vbap.sc: data_length = 2 + (numSets * (dim + (dim * dim) + (dim * dim))); + int dataLength = buf->samples; // loudspeaker data buffer length + int payload = dataLength - 2; // payload size without header + int stride = dim + 2 * dim * dim; // payload stride + int numSets = payload / stride; + + if (numSets <= 0 || (payload % stride) != 0) { + printf("VBAP error: Error in loudspeaker data. Size of the loudspeaker data buffer (%d) doesn't match what's expected for given the number of loudspeaker sets (%d) and the loudspeaker array dimension (%d). Bufnum: %i\n", + dataLength, numSets, dim, (int)fbufnum); + passing = false; + } + + if(!passing) { + unit->x_lsset_available = 0; + SETCALC(*ClearUnitOutputs); + ClearUnitOutputs(unit, 1); + return; + } + + /* ... loudspeaker data buffer is validated, we can now init vars and alloc memory */ + unit->x_lsset_available = 1; + + unit->x_dimension = dim; + unit->x_num_ls = num_ls; + unit->x_num_lssets = numSets; + + // uninitialized controls trigger re-calc in VBAP_calc_gain_factors: + // NaNs are not equal to any floating point number + unit->x_azi = unit->x_ele = unit->x_spread = std::numeric_limits::quiet_NaN(); - unit->x_set_inv_matx = (float**)RTAlloc(unit->mWorld, counter * sizeof(float*)); - unit->x_set_matx = (float**)RTAlloc(unit->mWorld, counter * sizeof(float*)); - unit->x_lsset = (int**)RTAlloc(unit->mWorld, counter * sizeof(int*)); + unit->final_gs = (float*)RTAlloc(unit->mWorld, numOutputs * sizeof(float)); + unit->m_chanamp = (float*)RTAlloc(unit->mWorld, numOutputs * sizeof(float)); - for(i=0; ix_set_inv_matx = (float**)RTAlloc(unit->mWorld, numSets * sizeof(float*)); + unit->x_set_matx = (float**)RTAlloc(unit->mWorld, numSets * sizeof(float*)); + unit->x_lsset = (int**)RTAlloc(unit->mWorld, numSets * sizeof(int*)); + + // TODO: why are these a fixed size of 3 x 3? are 2D sets treated as 3D? + for(i=0; ix_set_inv_matx[i] = (float*)RTAlloc(unit->mWorld, 9 * sizeof(float)); unit->x_set_matx[i] = (float*)RTAlloc(unit->mWorld, 9 * sizeof(float)); unit->x_lsset[i] = (int*)RTAlloc(unit->mWorld, 3 * sizeof(int)); } - // probably sets should be created with rtalloc - while(counter-- > 0){ + + /* Read data from buffer into vars */ + int setCounter = numSets; + while(setCounter-- > 0){ for(i=0; i < unit->x_dimension; i++){ unit->x_lsset[setpointer][i]=(int)buf->data[datapointer++]; } - for(i=0; i < unit->x_dimension*unit->x_dimension; i++){ unit->x_set_inv_matx[setpointer][i]=buf->data[datapointer++]; - /* post("%d",deb++); */ } - if(unit->x_dimension == 3){ + if(unit->x_dimension == 2 || unit->x_dimension == 3){ for(i=0; i < unit->x_dimension*unit->x_dimension; i++){ unit->x_set_matx[setpointer][i]=buf->data[datapointer++]; - } } @@ -692,52 +710,34 @@ static void VBAP_Ctor(VBAP* unit) #endif SETCALC(VBAP_next); - ZOUT0(0) = ZIN0(0); - unit->x_azi = ZIN0(2); - unit->x_ele = ZIN0(3); - unit->x_spread_base[0] = 0.0; - unit->x_spread_base[1] = 1.0; - unit->x_spread_base[2] = 0.0; - unit->x_spread = ZIN0(4); - - // calculate initial gain factors - float g[3]; - int ls[3]; - float *final_gs = unit->final_gs; - - if(unit->x_lsset_available ==1){ - vbap(g,ls, unit); - for(i=0;ix_ls_amount;i++) - final_gs[i]=0.0; - for(i=0;ix_dimension;i++){ - final_gs[ls[i]-1]=g[i]; - } - if(unit->x_spread != 0){ - spread_it(unit,final_gs); - } - - } else { - // if the ls data was bad, just set every gain to 0 and bail - for(i=0;ix_ls_amount;i++) - final_gs[i]=0.f; - } + // init gains + VBAP_calc_gain_factors(unit); + // init channel amps to avoid fade-in from zero + for(i=0;ix_num_ls;i++) + unit->m_chanamp[i] = unit->final_gs[i]; + + VBAP_next(unit, 1); // don't call the SIMD _next with one sample! } static void VBAP_Dtor(VBAP* unit) { - int counter = unit->x_lsset_amount; - RTFree(unit->mWorld, unit->final_gs); - for(int i=0; imWorld, unit->x_set_inv_matx[i]); - RTFree(unit->mWorld, unit->x_set_matx[i]); - RTFree(unit->mWorld, unit->x_lsset[i]); + if (unit->x_lsset_available > 0) { // only free if initialization occured + int counter = unit->x_num_lssets; + RTFree(unit->mWorld, unit->final_gs); + RTFree(unit->mWorld, unit->m_chanamp); + + for(int i=0; imWorld, unit->x_set_inv_matx[i]); + RTFree(unit->mWorld, unit->x_set_matx[i]); + RTFree(unit->mWorld, unit->x_lsset[i]); + } + + RTFree(unit->mWorld, unit->x_set_inv_matx); + RTFree(unit->mWorld, unit->x_set_matx); + RTFree(unit->mWorld, unit->x_lsset); } - - RTFree(unit->mWorld, unit->x_set_inv_matx); - RTFree(unit->mWorld, unit->x_set_matx); - RTFree(unit->mWorld, unit->x_lsset); } ////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/source/VBAPUGens/sc/HelpSource/Classes/VBAP.schelp b/source/VBAPUGens/sc/HelpSource/Classes/VBAP.schelp index 72c5d3db3..823de63d7 100644 --- a/source/VBAPUGens/sc/HelpSource/Classes/VBAP.schelp +++ b/source/VBAPUGens/sc/HelpSource/Classes/VBAP.schelp @@ -17,7 +17,7 @@ The number of output channels. argument:: in The signal to be panned. argument:: bufnum -A Buffer or its bufnum containing data calculated by an instance of VBAPSpeakerArray. Its number of channels must correspond to numChan above. +A Buffer or its bufnum containing data calculated by an instance of VBAPSpeakerArray. Its number of channels must correspond to numChan above. argument:: azimuth +/- 180 degrees from the median plane (i.e. straight ahead) argument:: elevation @@ -27,7 +27,6 @@ A value from 0-100. When 0, if the signal is panned exactly to a speaker locatio examples:: code:: -Server.default = s = Server.internal; // 2D a = VBAPSpeakerArray.new(2, [0, 45, 90, 135, 180, -135, -90, -45]); // 8 channel ring @@ -37,7 +36,7 @@ b = a.loadToBuffer; ( x = { |azi = 0, ele = 0, spr = 0| -VBAP.ar(8, PinkNoise.ar(0.2), b.bufnum, azi, ele, spr); + VBAP.ar(8, PinkNoise.ar(0.2), b.bufnum, azi, ele, spr); }.scope; ) @@ -58,9 +57,9 @@ b = Buffer.loadCollection(s, a.getSetsAndMatrices); ( // pan around the circle up and down x = { |azi = 0, ele = 0, spr = 0| -var source; -source = PinkNoise.ar(0.2); -VBAP.ar(16, source, b.bufnum, LFSaw.kr(0.5, 0).range(-180, 180) * -1, SinOsc.kr(3, 0).range(0, 14.97), spr); + var source; + source = PinkNoise.ar(0.2); + VBAP.ar(16, source, b.bufnum, LFSaw.kr(0.5, 0).range(-180, 180) * -1, SinOsc.kr(3, 0).range(0, 14.97), spr); }.play; ) @@ -88,53 +87,55 @@ c = UserView.new(w,Rect(0, 0, 400, 380)); c.canFocus = false; c.drawFunc = { - Color.grey(0.8).set; - // draw the speaker layout - Pen.translate(200,200); - ((actPoint.theta + (0.5pi)).wrap2(pi) * rtoang).round(0.01).asString.drawCenteredIn(Rect.aboutPoint(0@170, 30, 10), Font.new("Arial", 10), Color.grey(0.8)); - Pen.strokeOval(Rect.aboutPoint(0@0, 150, 150)); - Pen.rotate(pi); - speakerList.do({|spkr| - Pen.use({ - Pen.rotate(spkr[0] * atorad); - Pen.moveTo(0@170); - Pen.strokeRect(r = Rect.aboutPoint(0@170, 30, 10)); - if(spkr[0].abs < 90, { - Pen.use({ - Pen.translate(0, 170); - Pen.rotate(pi); - spkr[1].drawCenteredIn(Rect.aboutPoint(0@0, 30, 10), - GUI.font.new("Arial", 10), Color.grey(0.8)); - }); - },{ - spkr[1].drawCenteredIn(r, GUI.font.new("Arial", 10), Color.grey(0.8)); - }); - }); - }); - - Pen.moveTo(0@0); - - // draw the pan point - Pen.rotate(actPoint.theta + 0.5pi); - targPoint = Point(x, y) - Point(200, 200); - // trunc to avoid loops due to fp math - targRotate = (targPoint.theta - actPoint.theta).trunc(1e-15); - // wrap around - if(targRotate.abs > pi, {targRotate = (2pi - targRotate.abs) * targRotate.sign.neg}); - actRotate = targRotate.clip2(maxShiftPerFrame).trunc(1e-15); - actPoint = actPoint.rotate(actRotate); - Pen.rotate(actRotate); - Pen.lineTo(0@150); - Pen.stroke; - Pen.fillOval(Rect.aboutPoint(0@150, 7, 7)); - Pen.addWedge(0@0, 140, neg(e.value * 0.5) * atorad + 0.5pi, e.value * atorad); - Pen.stroke; - Color.grey(0.8).alpha_(0.1).set; - Pen.addWedge(0@0, 140, neg(e.value * 0.5) * atorad + 0.5pi, e.value * atorad); - Pen.fill; - - if((actRotate.abs > 0), {AppClock.sched(frameInterval, {w.refresh})}, {count = 0;}); - if(count%4 == 0, {panBus.set((actPoint.theta + (0.5pi)).wrap2(pi) * rtoang)}); + Color.grey(0.8).set; + // draw the speaker layout + Pen.translate(200,200); + ((actPoint.theta + (0.5pi)).wrap2(pi) * rtoang).round(0.01).asString.drawCenteredIn( + Rect.aboutPoint(0@170, 30, 10), Font.new("Arial", 10), Color.grey(0.8) + ); + Pen.strokeOval(Rect.aboutPoint(0@0, 150, 150)); + Pen.rotate(pi); + speakerList.do({|spkr| + Pen.use({ + Pen.rotate(spkr[0] * atorad); + Pen.moveTo(0@170); + Pen.strokeRect(r = Rect.aboutPoint(0@170, 30, 10)); + if(spkr[0].abs < 90, { + Pen.use({ + Pen.translate(0, 170); + Pen.rotate(pi); + spkr[1].drawCenteredIn(Rect.aboutPoint(0@0, 30, 10), + GUI.font.new("Arial", 10), Color.grey(0.8)); + }); + },{ + spkr[1].drawCenteredIn(r, GUI.font.new("Arial", 10), Color.grey(0.8)); + }); + }); + }); + + Pen.moveTo(0@0); + + // draw the pan point + Pen.rotate(actPoint.theta + 0.5pi); + targPoint = Point(x, y) - Point(200, 200); + // trunc to avoid loops due to fp math + targRotate = (targPoint.theta - actPoint.theta).trunc(1e-15); + // wrap around + if(targRotate.abs > pi, {targRotate = (2pi - targRotate.abs) * targRotate.sign.neg}); + actRotate = targRotate.clip2(maxShiftPerFrame).trunc(1e-15); + actPoint = actPoint.rotate(actRotate); + Pen.rotate(actRotate); + Pen.lineTo(0@150); + Pen.stroke; + Pen.fillOval(Rect.aboutPoint(0@150, 7, 7)); + Pen.addWedge(0@0, 140, neg(e.value * 0.5) * atorad + 0.5pi, e.value * atorad); + Pen.stroke; + Color.grey(0.8).alpha_(0.1).set; + Pen.addWedge(0@0, 140, neg(e.value * 0.5) * atorad + 0.5pi, e.value * atorad); + Pen.fill; + + if((actRotate.abs > 0), {AppClock.sched(frameInterval, {w.refresh})}, {count = 0;}); + if(count%4 == 0, {panBus.set((actPoint.theta + (0.5pi)).wrap2(pi) * rtoang)}); }; c.mouseMoveAction_({|v,inx,iny| x = inx; y = iny; w.refresh;}); c.mouseDownAction_({|v,inx,iny| x = inx; y = iny; w.refresh;}); @@ -149,13 +150,13 @@ a = VBAPSpeakerArray.new(2, speakerList.collect(_.first)); b = a.loadToBuffer; SynthDef('VBAP 5 chan', { |azi = 0, ele = 0, spr = 0, width = 60, vbapBuf| -var panned, source; -source = SinOsc.ar([440, 660], 0, Decay2.ar(Impulse.ar([1, 0.9]), 0.1, 0.2)); -azi = azi.circleRamp; -panned = VBAP.ar(5, source, vbapBuf, [azi - (0.5 * width), azi + (0.5 * width)], ele, spr); -// 'standard' channel order for 5.1 -[0, 1, 2, 4, 5].do({arg bus, i; Out.ar(bus, panned[0][i])}); -[0, 1, 2, 4, 5].do({arg bus, i; Out.ar(bus, panned[1][i])}); + var panned, source; + source = SinOsc.ar([440, 660], 0, Decay2.ar(Impulse.ar([1, 0.9]), 0.1, 0.2)); + azi = azi.circleRamp; + panned = VBAP.ar(5, source, vbapBuf, [azi - (0.5 * width), azi + (0.5 * width)], ele, spr); + // 'standard' channel order for 5.1 + [0, 1, 2, 4, 5].do({arg bus, i; Out.ar(bus, panned[0][i])}); + [0, 1, 2, 4, 5].do({arg bus, i; Out.ar(bus, panned[1][i])}); }).play(s, [vbapBuf: b.bufnum, azi: panBus.asMap, width: widthBus.asMap]); diff --git a/source/VBAPUGens/sc/vbap.sc b/source/VBAPUGens/sc/vbap.sc index 8615a90a3..1f4570eba 100644 --- a/source/VBAPUGens/sc/vbap.sc +++ b/source/VBAPUGens/sc/vbap.sc @@ -164,7 +164,7 @@ VBAPSpeakerArray { /* remove triangles which had crossing sides with smaller triangles or include loudspeakers*/ - //"triplet_amount before stripping: %\n".postf(sets.size); + //"numTriplets before stripping: %\n".postf(sets.size); sets = sets.reject({|set| i1 = set.chanOffsets[0]; j1 = set.chanOffsets[1]; @@ -172,7 +172,7 @@ VBAPSpeakerArray { (connections[i1][j1] == 0) || (connections[i1][k1] == 0) || (connections[j1][k1] == 0) || this.any_ls_inside_triplet(i1,j1,k1); }); - //"triplet_amount after stripping: %\n".postf(sets.size); + //"numTriplets after stripping: %\n".postf(sets.size); } lines_intersect { |i, j, k, l| @@ -320,18 +320,28 @@ VBAPSpeakerArray { var invdet; var lp1, lp2, lp3; var invmx; - var triplet_amount = 0, pointer,list_length=0; + var numTriplets, pointer, data_length; var result; + var dim; if(sets.isNil, { postln("define-loudspeakers: Not valid 3-D configuration"); ^nil; }); - triplet_amount = sets.size; - //"triplet_amount: %\n".postf(triplet_amount); - list_length = triplet_amount * 21 + 2; - result = FloatArray.newClear(list_length); + // Returned FLoatArray data structure: + // "header": + // layout dimentions: 2 or 3 (1) + // number of loudspeakers (1) + // triplet data (x numTriplets): + // channel indices of the triplet (3) + // inverse matrix for gain solving (3 x 3, flattened to 9) + // cartesian coords of each loudspeaker in the triplet (xyz x 3, flattened to 9) + // So buffer size is 1 + 1 + (numTriplets * (3 + 9 + 9)); + dim = 3; // 3D, loudspeaker sets are triplets + numTriplets = sets.size; // number of triplets + data_length = 2 + (numTriplets * (dim + (dim * dim) + (dim * dim))); + result = FloatArray.newClear(data_length); result[0] = dim; result[1] = numSpeakers; @@ -391,10 +401,10 @@ VBAPSpeakerArray { var atorad = (2 * pi / 360); var sorted_lss; var exist; - var amount=0; + var numPairs = 0; // number of loudspeaker pairs var inv_mat; var mat; - var list_length; + var data_length; var result; var pointer; @@ -416,7 +426,7 @@ VBAPSpeakerArray { if(this.calc_2D_inv_tmatrix(speakers[sorted_lss[i]].azi, speakers[sorted_lss[i+1]].azi, inv_mat[i], mat[i]),{ exist[i]=1; - amount = amount + 1; + numPairs = numPairs + 1; }); }); }); @@ -427,13 +437,22 @@ VBAPSpeakerArray { speakers[sorted_lss[0]].azi, inv_mat[numSpeakers-1], mat[numSpeakers-1]), { exist[numSpeakers-1]=1; - amount = amount + 1; + numPairs = numPairs + 1; }); }); /* Output */ - list_length= amount * 10 + 2; - result = Array.newClear(list_length); + // data structure: + // header: + // layout dimention: 2 (1) + // number of loudspeakers (1) + // pair data (x numPairs): + // channel indices of the pair (2) + // inverse matrix for gain solving (2 x 2, flattened to 4) + // forward matrix for inversion (2 x 2, flattened to 4) + // So buffer size is 1 + 1 + (numPairs * (2 + 4 + 4)); + data_length = 2 + (numPairs * (dim + (dim * dim) + (dim * dim))); + result = Array.newClear(data_length); result[0] = dim; result[1] = numSpeakers;