/// <summary> /// 初期設定する /// </summary> /// <param name="room"></param> public void Start(WWRoom room) { var leftEarPos = room.ListenerEarPos(0); var rightEarPos = room.ListenerEarPos(1); var leftSpeakerPos = room.SpeakerPos(0); var rightSpeakerPos = room.SpeakerPos(1); // 左スピーカーから左の耳に音が届く var ll = leftEarPos - leftSpeakerPos; var llN = ll; llN.Normalize(); // エネルギーは、距離の2乗に反比例する // 振幅は、距離の1乗に反比例 // ということにする。 mLeftSpeakerToLeftEar.Add(new WWFirCoefficient(ll.Length / SoundSpeed, llN, 1.0 / ll.Length, true)); // 右スピーカーから右の耳に音が届く var rr = rightEarPos - rightSpeakerPos; var rrN = rr; rrN.Normalize(); mRightSpeakerToRightEar.Add(new WWFirCoefficient(rr.Length / SoundSpeed, rrN, 1.0 / rr.Length, true)); double gain = 1.0; if (WallReflectionType == ReflectionType.Specular) { // なんとなく、高音は逆の耳に届きにくい感じ。 gain = 1.0 / 1.414; } // 左スピーカーから右の耳に音が届く。 // 振幅が-3dBくらいになる。 double attenuationDecibel = -3.0; double attenuationMagnitude = Math.Pow(10.0, attenuationDecibel / 20.0); var lr = rightEarPos - leftSpeakerPos; var lrN = lr; lrN.Normalize(); mLeftSpeakerToRightEar.Add(new WWFirCoefficient(lr.Length / SoundSpeed, lrN, gain * attenuationMagnitude / lr.Length, true)); var rl = leftEarPos - rightSpeakerPos; var rlN = rl; rlN.Normalize(); mRightSpeakerToLeftEar.Add(new WWFirCoefficient(rl.Length / SoundSpeed, rlN, gain * attenuationMagnitude / rl.Length, true)); // 1本のレイがそれぞれのスピーカーリスナー組に入る。 mRouteCount[0] = 1; mRouteCount[1] = 1; }
/// <summary> /// スピーカーから耳に届く音がたどる経路を調べる。 /// </summary> /// <param name="room"></param> /// <param name="earCh">耳 0:左耳, 1:右耳</param> public void Trace(WWRoom room, ReflectionType reflectionType, int earCh) { var route = new WWRoute(earCh); // 耳の位置 var rayPos = room.ListenerEarPos(earCh); var earDir = room.ListenerEarDir(earCh); Vector3D rayDir = RayGen(earDir); //耳からrayが発射して、部屋の壁に当たる // 音が耳に向かう方向。 Vector3D soundDir = -rayDir; var accumReflectionGain = new double[] {1.0, 1.0}; for (int i = 0; i < MaxReflectionCount; ++i) { Point3D hitPos; Vector3D hitSurfaceNormal; double rayLength; if (!room.RayIntersection(rayPos, rayDir, out hitPos, out hitSurfaceNormal, out rayLength)) { // 終わり。 break; } // 1.0 - 反射率の確率で、計算を打ち切る。 // たとえば反射率0.8の壁にRayが10本入射すると、8本のRayが強度を100%保ったまま反射する。 if (WallReflectionRatio < NextDouble()) { break; } // スピーカーから耳への道のりを計算する。 var lineSegment = new WWLineSegment(rayPos, rayDir, rayLength, 1.0f /* 仮 Intensity */ ); { int speakerCh = earCh; var distance = CalcRouteDistance(room, speakerCh, route, lineSegment, hitPos); double gain = CalcReflectionGain(reflectionType, room, speakerCh, hitPos, rayDir, hitSurfaceNormal); accumReflectionGain[0] *= gain; var coeffS = new WWFirCoefficient(distance / SoundSpeed, soundDir, accumReflectionGain[0] / distance, false); lineSegment.Intensity = coeffS.Gain; if (1.0 / distance < SMALL_GAIN_THRESHOLD) { break; } if (SMALL_GAIN_THRESHOLD <= coeffS.Gain) { StoreCoeff(earCh, earCh, coeffS); } } { int speakerCh = (earCh == 0) ? 1 : 0; var distance = CalcRouteDistance(room, speakerCh, route, lineSegment, hitPos); double gain = CalcReflectionGain(reflectionType, room, speakerCh, hitPos, rayDir, hitSurfaceNormal); accumReflectionGain[1] *= gain; var coeffD = new WWFirCoefficient(distance / SoundSpeed, soundDir, accumReflectionGain[1] / distance, false); if (SMALL_GAIN_THRESHOLD <= coeffD.Gain) { StoreCoeff(earCh, speakerCh, coeffD); } } route.Add(lineSegment); rayPos = hitPos; // 反射後の出射方向rayDir switch (reflectionType) { case ReflectionType.Diffuse: rayDir = RayGen(hitSurfaceNormal); break; case ReflectionType.Specular: rayDir = SpecularReflection(rayDir, hitSurfaceNormal); break; default: System.Diagnostics.Debug.Assert(false); break; } } // routeの中に、1つもlineSegmentが入っていないことがある。 mRouteList.Add(route); Interlocked.Increment(ref mRouteCount[earCh]); }
/// <summary> /// 初期設定する /// </summary> /// <param name="room"></param> public void Start(WWRoom room) { var leftEarPos = room.ListenerEarPos(0); var rightEarPos = room.ListenerEarPos(1); var leftSpeakerPos = room.SpeakerPos(0); var rightSpeakerPos = room.SpeakerPos(1); // 左スピーカーから左の耳に音が届く var ll = leftEarPos - leftSpeakerPos; var llN = ll; llN.Normalize(); // エネルギーは、距離の2乗に反比例する // 振幅は、距離の1乗に反比例 // ということにする。 mLeftSpeakerToLeftEar.Add(new WWFirCoefficient(ll.Length / SoundSpeed, llN, 1.0 / ll.Length, true)); // 右スピーカーから右の耳に音が届く var rr = rightEarPos - rightSpeakerPos; var rrN = rr; rrN.Normalize(); mRightSpeakerToRightEar.Add(new WWFirCoefficient(rr.Length / SoundSpeed, rrN, 1.0 / rr.Length, true)); double gain = 1.0; if (WallReflectionType == ReflectionType.Specular) { // なんとなく、高音は逆の耳に届きにくい感じ。 gain = 1.0/1.414; } // 左スピーカーから右の耳に音が届く。 // 振幅が-3dBくらいになる。 double attenuationDecibel = -3.0; double attenuationMagnitude = Math.Pow(10.0, attenuationDecibel / 20.0); var lr = rightEarPos - leftSpeakerPos; var lrN = lr; lrN.Normalize(); mLeftSpeakerToRightEar.Add(new WWFirCoefficient(lr.Length / SoundSpeed, lrN, gain * attenuationMagnitude / lr.Length, true)); var rl = leftEarPos - rightSpeakerPos; var rlN = rl; rlN.Normalize(); mRightSpeakerToLeftEar.Add(new WWFirCoefficient(rl.Length / SoundSpeed, rlN, gain * attenuationMagnitude / rl.Length, true)); // 1本のレイがそれぞれのスピーカーリスナー組に入る。 mRouteCount[0] = 1; mRouteCount[1] = 1; }
/// <summary> /// スピーカーから耳に届く音がたどる経路を調べる。 /// </summary> /// <param name="room"></param> /// <param name="earCh">耳 0:左耳, 1:右耳</param> public void Trace(WWRoom room, ReflectionType reflectionType, int earCh) { var route = new WWRoute(earCh); // 耳の位置 var rayPos = room.ListenerEarPos(earCh); var earDir = room.ListenerEarDir(earCh); Vector3D rayDir = RayGen(earDir); //耳からrayが発射して、部屋の壁に当たる // 音が耳に向かう方向。 Vector3D soundDir = -rayDir; var accumReflectionGain = new double[] { 1.0, 1.0 }; for (int i = 0; i < MaxReflectionCount; ++i) { Point3D hitPos; Vector3D hitSurfaceNormal; double rayLength; if (!room.RayIntersection(rayPos, rayDir, out hitPos, out hitSurfaceNormal, out rayLength)) { // 終わり。 break; } // 1.0 - 反射率の確率で、計算を打ち切る。 // たとえば反射率0.8の壁にRayが10本入射すると、8本のRayが強度を100%保ったまま反射する。 if (WallReflectionRatio < NextDouble()) { break; } // スピーカーから耳への道のりを計算する。 var lineSegment = new WWLineSegment(rayPos, rayDir, rayLength, 1.0f /* 仮 Intensity */); { int speakerCh = earCh; var distance = CalcRouteDistance(room, speakerCh, route, lineSegment, hitPos); double gain = CalcReflectionGain(reflectionType, room, speakerCh, hitPos, rayDir, hitSurfaceNormal); accumReflectionGain[0] *= gain; var coeffS = new WWFirCoefficient(distance / SoundSpeed, soundDir, accumReflectionGain[0] / distance, false); lineSegment.Intensity = coeffS.Gain; if (1.0 / distance < SMALL_GAIN_THRESHOLD) { break; } if (SMALL_GAIN_THRESHOLD <= coeffS.Gain) { StoreCoeff(earCh, earCh, coeffS); } } { int speakerCh = (earCh == 0) ? 1 : 0; var distance = CalcRouteDistance(room, speakerCh, route, lineSegment, hitPos); double gain = CalcReflectionGain(reflectionType, room, speakerCh, hitPos, rayDir, hitSurfaceNormal); accumReflectionGain[1] *= gain; var coeffD = new WWFirCoefficient(distance / SoundSpeed, soundDir, accumReflectionGain[1] / distance, false); if (SMALL_GAIN_THRESHOLD <= coeffD.Gain) { StoreCoeff(earCh, speakerCh, coeffD); } } route.Add(lineSegment); rayPos = hitPos; // 反射後の出射方向rayDir switch (reflectionType) { case ReflectionType.Diffuse: rayDir = RayGen(hitSurfaceNormal); break; case ReflectionType.Specular: rayDir = SpecularReflection(rayDir, hitSurfaceNormal); break; default: System.Diagnostics.Debug.Assert(false); break; } } // routeの中に、1つもlineSegmentが入っていないことがある。 mRouteList.Add(route); Interlocked.Increment(ref mRouteCount[earCh]); }