Lateral Reflections in Rectangular Rooms (Code)

Several people have asked for the code that I used in a previous post to calculate and plot first-reflection delay times as a function of listener- and loudspeaker-placement.  So here it is.

Running the script below will produce pdf or postscript graphics output that looks like this:

codetest

The graph shows a rectangular room plan with superimposed contours that show, for a fixed listening position, the delay time for first-order sound reflections as a function of loudspeaker placement.  Each red curve indicates placements that yield exactly 6ms delay for reflection from a particular wall.  The dashed lines are a guide: a pair of loudspeakers placed symmetrically on these lines will form an equilateral triangle with the listener.  Also shown is the largest possible stereo separation (1.86m in the diagram above) that yields at least 6ms delay.

The script is written in the asymptote vector graphics language.  The variables defining room dimensions and listening position are near the top.  Change these to match your own room.  For now the code only works with rectangular rooms.

/* relftime.asy
Richard Taylor 03.11.2014
An asymptote (asymptote.sourceforge.net) script.

This draws a rectangular room plan with superimposed contours that
show, for any given loudspeaker placement, the resulting arrival time
delay (for a fixed listening position) between the direct sound and
the first lateral reflection.

Additionally, for each of the four walls a red contour is drawn to
show the loudspeaker placements that give exactly 6ms delay for
reflection from that wall. The shaded region indicates speaker
placements for ≥6ms delay from all walls.

The listening position is marked with a green triangle. Dashed lines
are shown as a guide: a pair of loudspeaker speakers placed
symmetrically on these lines will form an equilateral triangle with
the listening position. */

import contour;
import graph;
unitsize(3cm);

// room dimensions [m]:
real Lx = 4.5;
real Ly = 5.5;

// listening position [m] (relative to back-left corner):
real ly = 1.5;
real lx = 0.5*Lx;

// target minimum delay [ms]:
real mindelay = 6.0;

pair listener = (lx,ly);

// function to calculate arrival time difference [ms] for direct and
// reflected sound.
// x1 = distance from source to wall [m]
// x2 = distance from listener to wall [m]
// y = source-listener lateral separation parallel to wall [m]
real rt(real x1, real x2, real y) {
real dd = sqrt( (x1-x2)^2 + y^2 ); // direct-sound path length
real dr = sqrt( (x1+x2)^2 + y^2 ); // reflected-sound path length
return (dr-dd)/0.343;
}

real rtT(real x, real y) { // refl. from top, source at (x,y)
return rt( Ly-y, Ly-ly, x-lx );
}

real rtB(real x, real y) { // refl. from bottom, source at (x,y)
return rt( y, ly, x-lx );
}

real rtR(real x, real y) { //  refl. from right, source at (x,y)
return rt( Lx-x, Lx-lx, y-ly );
}

real rtL(real x, real y) { // refl. from right, source at (x,y)
return rt( x, lx, y-ly );
}

real rtmin(real x, real y) { // min. delay for all boundaries
  return min( rtT(x,y), rtB(x,y), rtL(x,y), rtR(x,y) );
}

// shade region where delay is at least 6ms:
guide[][] g=contour(rtmin,(0,0),(Lx,Ly),new real[] {mindelay});
fill(g[0],mediumgrey);

// guide lines for equilateral loudspeaker placement:
picture equiguide=new picture;
real L=sqrt(Lx^2+Ly^2);
path equilateral = (listener+L*dir(120))--listener--(listener+L*dir(60));
draw(equiguide, equilateral, dashed );
clip(equiguide,(0,0)--(Lx,0)--(Lx,Ly)--(0,Ly)--cycle);
add(equiguide);

// mark listening position:
draw( listener, marker(scale(2mm)*polygon(3),FillDraw(green) ) );

// draw 6ms contour for each reflection:
draw( new Label[] {Label("FRONT WALL",Relative(0.6),Center,UnFill(1bp))},
contour(rtT,(0,0),(Lx,Ly),new real[] {mindelay}), red+linewidth(2) );
draw( new Label[] {Label("REAR WALL",Relative(0.4),Center,UnFill(1bp))},
contour(rtB,(0,0),(Lx,Ly),new real[] {mindelay}), red+linewidth(2) );
draw( new Label[] {Label("LEFT WALL",Relative(0.15),Center,UnFill(1bp))},
contour(rtL,(0,0),(Lx,Ly),new real[] {mindelay}), red+linewidth(2) );
draw( new Label[] {Label("RIGHT WALL",Relative(0.15),Center,UnFill(1bp))},
contour(rtR,(0,0),(Lx,Ly),new real[] {mindelay}), red+linewidth(2) );

// add contours for first-reflection delay times:
int n=5;
real[] c=new real[n];
for(int i=0; i < n; ++i) c[i]=i+1; // contour levels

Label[] Labels=sequence(new Label(int i) {
return Label(c[i] != 0 ? format("$%0.0f$\,ms" ,c[i]) : "",
        Relative(0.49 - 0.01*i/n),(0,0), UnFill(1bp));
},c.length);

draw( Labels, contour(rtmin,(0,0),(Lx,Ly),c, nx=100, ny=100) );

// draw room boundaries:
draw( (0,0)--(Lx,0)--(Lx,Ly)--(0,Ly)--cycle, blue+linewidth(2) );

// add some axes:
xaxis(Label("$x$ [meters]",Relative(0.5)),BottomTop,RightTicks,xmin=0);
yaxis(Label("$y$ [meters]",Relative(0.5)),LeftRight,LeftTicks,ymin=0);

// show max. stereo separation with equilateral placement and 6ms delay:
path contour6 = g[0][0];
pair[] furthestpoints = intersectionpoints(equilateral, contour6);
pair p1 = furthestpoints[0];  pair p2 = furthestpoints[1];
draw( Label(format("$%0.2f$\,m", length(p2-p1)), LeftSide),
      p1--p2, dashed, Arrows );

Update 13.11.2014: Added code to show maximum stereo separation with equilateral placement and 6ms delay.

Update 02.03.2015: Fixed a bug that affected showing the maximum stereo separation.

8 thoughts on “Lateral Reflections in Rectangular Rooms (Code)

  1. How do I change speaker position ?
    I understand about the editing of “room dimensions” & “listening position”.

    // room dimensions [m]:
    real Lx = 4.5; -> real Lx = 3.45;
    real Ly = 5.5;

    // listening position [m] (relative to back-left corner):
    real ly = 1.5; -> real ly = 2.5;
    real lx = 0.5*Lx;

  2. Great reading on your blog. For those who wish to use your script here is a quick guide:

    Download: ProTeXt-3.1.4-020114.exe 1.6GB, asymptote-2.32-setup.exe 5MB
    Install the first one chosing MiKTex > Basic
    Install asymptote and make a config.asy file in its program files folder with notepad:

    Put these two lines in the file
    import settings;
    texpath=”C:\Program Files\MiKTex 2.9\miktex\bin\x64″;
    you might have to start notepad with administrator permissions to save this file. Be sure tho choose Save as type: all files *.*

    Double click the relftime.asy and it will make you a relftime.eps file and two logs

    Viewing the EPS: If you have irfanview then get the postscript.dll file plugin and install it. Otherwise just drag and drop into microsoft word – looks ugly but does the trick. Deluxe viewing install ghostscript and ghostscript viewer. Set ghostscript viewer to Options > EPS clip to stop it from clipping off the right side of large rooms.

    gs915w32.exe

  3. Thanks lots Richard, for posting the script!

    Now I have to admit my defeat, that I’ve followed Chris’s instructions closely, and still only have errors not room graphs.

    Win7 64 system, followed steps up to installing GS, get error msgs when running refltime.asy.

    config.asy: 2.9: invalid token
    config.asy: 2.11: syntax error
    error: could not load module ‘config.asy’

    Tried many things, always using Admin mode with Notepad, saving all files… tried saving as ASCI, Unicode types. Thought I might have it when I noticed the path in Chris’s config.asy is slightly different from the MikTeX install dir, with no capital X at the end of MikTeX… but no.

    Hope someone can show me the error of my ways here….

    • I don’t have experience running this stuff under Windows (I use a linux machine for all my work so TeX, ghostscript, etc. are set up to work out-of-the-box) so sorry I can’t help here. Perhaps someone else can weigh in?

  4. config.asy: 2.9: invalid token
    config.asy: 2.11: syntax error
    error: could not load module ‘config.asy’

    the problem is with line 2 character 9 in config.asy. Copy pasting from my message results in an incorrect double quote (the left double quote). Just replace the funny double quote with a real one.

Leave a Reply

Your email address will not be published. Required fields are marked *