Source Code Cross Referenced for JSPositionalSample.java in  » 6.0-JDK-Modules » java-3d » com » sun » j3d » audioengines » javasound » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » java 3d » com.sun.j3d.audioengines.javasound 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $RCSfile: JSPositionalSample.java,v $
0003:         *
0004:         * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * Redistribution and use in source and binary forms, with or without
0007:         * modification, are permitted provided that the following conditions
0008:         * are met:
0009:         *
0010:         * - Redistribution of source code must retain the above copyright
0011:         *   notice, this list of conditions and the following disclaimer.
0012:         *
0013:         * - Redistribution in binary form must reproduce the above copyright
0014:         *   notice, this list of conditions and the following disclaimer in
0015:         *   the documentation and/or other materials provided with the
0016:         *   distribution.
0017:         *
0018:         * Neither the name of Sun Microsystems, Inc. or the names of
0019:         * contributors may be used to endorse or promote products derived
0020:         * from this software without specific prior written permission.
0021:         *
0022:         * This software is provided "AS IS," without a warranty of any
0023:         * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024:         * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025:         * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026:         * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027:         * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028:         * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029:         * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030:         * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031:         * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032:         * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033:         * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034:         * POSSIBILITY OF SUCH DAMAGES.
0035:         *
0036:         * You acknowledge that this software is not designed, licensed or
0037:         * intended for use in the design, construction, operation or
0038:         * maintenance of any nuclear facility.
0039:         *
0040:         * $Revision: 1.4 $
0041:         * $Date: 2007/02/09 17:20:03 $
0042:         * $State: Exp $
0043:         */
0044:
0045:        /*
0046:         * Java Sound PositionalSample object
0047:         *
0048:         * IMPLEMENTATION NOTE: The JavaSoundMixer is incomplete and really needs
0049:         * to be rewritten.
0050:         */
0051:
0052:        package com.sun.j3d.audioengines.javasound;
0053:
0054:        import javax.media.j3d.*;
0055:        import javax.vecmath.*;
0056:        import com.sun.j3d.audioengines.*;
0057:
0058:        /**
0059:         * The PostionalSample Class defines the data and methods associated with a 
0060:         * PointSound sample played thru the AudioDevice.
0061:         */
0062:
0063:        class JSPositionalSample extends JSSample {
0064:
0065:            // maintain fields for stereo channel rendering
0066:            float leftGain = 1.0f; // scale factor
0067:            float rightGain = 1.0f; // scale factor
0068:            int leftDelay = 0; // left  InterauralTimeDifference in millisec
0069:            int rightDelay = 0; // right ITD in millisec
0070:            // fields for reverb channel
0071:
0072:            // debug flag for the verbose Doppler calculation methods
0073:            static final protected boolean dopplerFlag = true;
0074:
0075:            /**
0076:             * For positional and directional sounds, TWO Hae streams or clips
0077:             * are allocated, one each for the left and right channels, played at
0078:             * a different (delayed) time and with a different gain value.
0079:             */
0080:            int secondIndex = NULL_SAMPLE;
0081:            /**
0082:             * A third sample for control of reverb of the stream/clip is openned
0083:             * and maintained for all directional/positional sounds.
0084:             * For now, even if no aural attributes (using reverb) are active, 
0085:             * a reverb channel is always started with the other two. A sound could 
0086:             * be started without reverb and then reverb added later, but since there
0087:             * is no way to offset properly into all sounds (considering non-cached
0088:             * and nconsistent rate-changes during playing) this third sound is
0089:             * always allocated and started.
0090:             */
0091:            int reverbIndex = NULL_SAMPLE;
0092:
0093:            /**
0094:             * Save ear positions transformed into VirtualWorld coords from Head coords
0095:             * These default positions are used when the real values cannot queried
0096:             */
0097:            Point3f xformLeftEar = new Point3f(-0.09f, -0.03f, 0.095f);
0098:            Point3f xformRightEar = new Point3f(0.09f, -0.03f, 0.095f);
0099:            // Z axis in head space - looking into the screen
0100:            Vector3f xformHeadZAxis = new Vector3f(0.0f, 0.0f, -1.0f); // Va
0101:
0102:            /**
0103:             * Save vectors from source source position to transformed ear parameters
0104:             */
0105:            Vector3f sourceToCenterEar = new Vector3f(); // Vh
0106:            Vector3f sourceToRightEar = new Vector3f(); // Vf or Vc
0107:            Vector3f sourceToLeftEar = new Vector3f(); // Vf or Vc
0108:
0109:            boolean averageDistances = false;
0110:            long deltaTime = 0;
0111:            double sourcePositionChange = -1.0;
0112:            double headPositionChange = -1.0;
0113:
0114:            /*
0115:             * Maintain the last locations of sound and head as well as time the
0116:             * sound was last processed.
0117:             * Process delta distance and time as part of Doppler calculations.
0118:             */
0119:            static int MAX_DISTANCES = 4;
0120:            int numDistances = 0;
0121:            // TODO: time is based on changes to position!!! only
0122:            // TODO: must shap shot when either Position OR ear changes!!!
0123:            // TODO: must grab all changes to VIEW parameters (could change ear)!!!
0124:            //          not just when pointer to View changes!!
0125:            long[] times = new long[MAX_DISTANCES];
0126:            Point3f[] positions = new Point3f[MAX_DISTANCES]; // xformed sound source positions
0127:            Point3f[] centerEars = new Point3f[MAX_DISTANCES]; // xformed center ear positions
0128:            /*
0129:             * a set of indices (first, last, and current) are maintained to point
0130:             * into the above arrays
0131:             */
0132:            int firstIndex = 0;
0133:            int lastIndex = 0;
0134:            int currentIndex = 0;
0135:
0136:            /*
0137:             * Allow changes in Doppler rate only small incremental values otherwise
0138:             * you hear skips in the pitch of a sound during playback.
0139:             * When playback is faster, allow delta changes:
0140:             *   (diff in Factor for octave (1.0))/(12 1/2-steps))*(1/4) of half-step 
0141:             * When playback is slower, allow delta changes:
0142:             *   (diff in Factor for octave (0.5))/(12 1/2-steps))*(1/4) of half-step 
0143:             */
0144:            double lastRequestedDopplerRateRatio = -1.0f;
0145:            double lastActualDopplerRateRatio = -1.0f;
0146:            static double maxRatio = 256.0f; // 8 times higher/lower
0147:            /*
0148:             * denotes movement of sound away or towards listener
0149:             */
0150:            static int TOWARDS = 1;
0151:            static int NO_CHANGE = 0;
0152:            static int AWAY = -1;
0153:
0154:            /*
0155:             * Process request for Filtering fields
0156:             */
0157:            boolean filterFlag = false;
0158:            float filterFreq = -1.0f;
0159:
0160:            /*
0161:             * Construct a new audio device Sample object
0162:             */
0163:            public JSPositionalSample() {
0164:                super ();
0165:                if (debugFlag)
0166:                    debugPrint("JSPositionalSample constructor");
0167:                // initiallize circular buffer for averaging distance values
0168:                for (int i = 0; i < MAX_DISTANCES; i++) {
0169:                    positions[i] = new Point3f();
0170:                    centerEars[i] = new Point3f(0.09f, -0.03f, 0.095f);
0171:                }
0172:                clear();
0173:            }
0174:
0175:            // TODO: get/set secondChannel to JSStream/Clip/MIDI
0176:            // TODO: get/set reverbChannel to JSStream/Clip/MIDI
0177:            /*
0178:             * Process request for Filtering fields
0179:             */
0180:            boolean getFilterFlag() {
0181:                return filterFlag;
0182:            }
0183:
0184:            float getFilterFreq() {
0185:                return filterFreq;
0186:            }
0187:
0188:            /**
0189:             * Clears the fields associated with sample data for this sound, and 
0190:             * frees any device specific data associated with this sample.
0191:             */
0192:            public void clear() {
0193:                if (debugFlag)
0194:                    debugPrint("JSPositionalSample.clear() enter");
0195:                super .clear();
0196:                leftGain = 1.0f;
0197:                rightGain = 1.0f;
0198:                leftDelay = 0;
0199:                rightDelay = 0;
0200:                xformLeftEar.set(-0.09f, -0.03f, 0.095f);
0201:                xformRightEar.set(0.09f, -0.03f, 0.095f);
0202:                // Z axis in head space - looking into the screen
0203:                xformHeadZAxis.set(0.0f, 0.0f, -1.0f); // Va
0204:                sourceToCenterEar.set(0.0f, 0.0f, 0.0f); // Vh
0205:                sourceToRightEar.set(0.0f, 0.0f, 0.0f); // Vf or Vc
0206:                sourceToLeftEar.set(0.0f, 0.0f, 0.0f); // Vf or Vc
0207:                reset();
0208:                if (debugFlag)
0209:                    debugPrint("JSPositionalSample.clear() exit");
0210:            }
0211:
0212:            /**
0213:             * Reset time and count based fields associated with sample data 
0214:             * for this sound
0215:             */
0216:            void reset() {
0217:                if (debugFlag)
0218:                    debugPrint("JSPositionalSample.reset() enter");
0219:                super .reset();
0220:                averageDistances = false; // denotes not previously processed
0221:                deltaTime = 0;
0222:                sourcePositionChange = -1.0;
0223:                headPositionChange = -1.0;
0224:                rateRatio = 1.0f;
0225:                numDistances = 0;
0226:                averageDistances = false;
0227:                if (debugFlag)
0228:                    debugPrint("JSPositionalSample.reset() exit");
0229:            }
0230:
0231:            // increments index counters and bumps index numbers if the end of
0232:            // the circular buffer is reached
0233:            void incrementIndices() {
0234:                int maxIndex = MAX_DISTANCES - 1;
0235:                if (numDistances < maxIndex) {
0236:                    averageDistances = false;
0237:                    currentIndex = numDistances;
0238:                    lastIndex = currentIndex - 1;
0239:                    firstIndex = 0;
0240:                    numDistances++;
0241:                } else if (numDistances == maxIndex) {
0242:                    // we filled the data buffers completely and are ready to
0243:                    // calculate averages
0244:                    averageDistances = true;
0245:                    currentIndex = maxIndex;
0246:                    lastIndex = currentIndex - 1;
0247:                    firstIndex = 0;
0248:                    numDistances++;
0249:                } else if (numDistances > maxIndex) {
0250:                    // increment each counter and loop around
0251:                    averageDistances = true;
0252:                    currentIndex++;
0253:                    lastIndex++;
0254:                    firstIndex++;
0255:                    currentIndex %= MAX_DISTANCES;
0256:                    lastIndex %= MAX_DISTANCES;
0257:                    firstIndex %= MAX_DISTANCES;
0258:                }
0259:            }
0260:
0261:            // Not only do we transform position but delta time is calculated and
0262:            // old transformed position is saved
0263:            // Average the last MAX_DISTANCES delta time and change in position using
0264:            // an array for both and circlularly storing the time and distance values
0265:            // into this array.
0266:            // Current transformed position and time in stored into maxIndex of their
0267:            // respective arrays.
0268:            void setXformedPosition() {
0269:                Point3f newPosition = new Point3f();
0270:                if (debugFlag)
0271:                    debugPrint("*** setXformedPosition");
0272:                // xform Position 
0273:                if (getVWrldXfrmFlag()) {
0274:                    if (debugFlag)
0275:                        debugPrint("    Transform set so transform pos");
0276:                    vworldXfrm.transform(position, newPosition);
0277:                } else {
0278:                    if (debugFlag)
0279:                        debugPrint("    Transform NOT set so pos => xformPos");
0280:                    newPosition.set(position);
0281:                }
0282:                // store position and increment indices ONLY if theres an actual change
0283:                if (newPosition.x == positions[currentIndex].x
0284:                        && newPosition.y == positions[currentIndex].y
0285:                        && newPosition.z == positions[currentIndex].z) {
0286:                    if (debugFlag)
0287:                        debugPrint("    No change in pos, so don't reset");
0288:                    return;
0289:                }
0290:
0291:                incrementIndices();
0292:                // store new transformed position
0293:                times[currentIndex] = System.currentTimeMillis();
0294:                positions[currentIndex].set(newPosition);
0295:                if (debugFlag)
0296:                    debugPrint("           xform(sound)Position -"
0297:                            + " positions[" + currentIndex + "] = ("
0298:                            + positions[currentIndex].x + ", "
0299:                            + positions[currentIndex].y + ", "
0300:                            + positions[currentIndex].z + ")");
0301:
0302:                // since this is a change to the sound position and not the
0303:                // head save the last head position into the current element
0304:                if (numDistances > 1)
0305:                    centerEars[currentIndex].set(centerEars[lastIndex]);
0306:
0307:            }
0308:
0309:            /**
0310:             * Set Doppler effect Rate 
0311:             *
0312:             * Calculate the rate of change in for the head and sound
0313:             * between the two time stamps (last two times position or
0314:             * VirtualWorld transform was updated).
0315:             * First determine if the head and sound source are moving 
0316:             * towards each other (distance between them is decreasing), 
0317:             * moving away from each other (distance between them is
0318:             * increasing), or no change (distance is the same, not moving
0319:             * or moving the same speed/direction).
0320:             * The following equation is used for determining the change in frequency -
0321:             *     If there has been a change in the distance between the head and sound:
0322:             *
0323:             *         f' =  f * frequencyScaleFactor * velocityRatio
0324:             *
0325:             *     For no change in the distance bewteen head and sound, velocityRatio is 1:
0326:             *
0327:             *         f' =  f
0328:             *
0329:             *     For head and sound moving towards each other, velocityRatio (> 1.0) is:
0330:             *
0331:             *         |  speedOfSound*rollOff  +   velocityOfHead*velocityScaleFactor  |
0332:             *         |  ------------------------------------------------------------- |
0333:             *         |  speedOfSound*rollOff  -  velocityOfSource*velocityScaleFactor |
0334:             * 
0335:             *     For head and sound moving away from each other, velocityRatio (< 1.0) is:
0336:             * 
0337:             *         |  speedOfSound*rollOff  -   velocityOfHead*velocityScaleFactor  |
0338:             *         |  ------------------------------------------------------------- |
0339:             *         |  speedOfSound*rollOff  +  velocityOfSource*velocityScaleFactor |
0340:             *
0341:             * where frequencyScaleFactor, rollOff, velocityScaleFactor all come from
0342:             * the active AuralAttributes parameters.
0343:             * The following special cases must be test for AuralAttribute parameters:
0344:             *   rolloff  
0345:             *       Value MUST be > zero for any sound to be heard!
0346:             *       If value is zero, all sounds affected by AuralAttribute region are silent.
0347:             *   velocityScaleFactor 
0348:             *      Value MUST be > zero for any sound to be heard!
0349:             *      If value is zero, all sounds affected by AuralAttribute region are paused.
0350:             *   frequencyScaleFactor 
0351:             *      Value of zero disables Doppler calculations:
0352:             *         Sfreq' = Sfreq * frequencyScaleFactor
0353:             *
0354:             * This rate is passed to device drive as a change to playback sample
0355:             * rate, in this case the frequency need not be known.
0356:             *
0357:             * Return value of zero denotes no change
0358:             * Return value of -1 denotes ERROR
0359:             */
0360:            float calculateDoppler(AuralParameters attribs) {
0361:                double sampleRateRatio = 1.0;
0362:                double headVelocity = 0.0; // in milliseconds
0363:                double soundVelocity = 0.0; // in milliseconds
0364:                double distanceSourceToHead = 0.0; // in meters
0365:                double lastDistanceSourceToHead = 0.0; // in meters
0366:                float speedOfSound = attribs.SPEED_OF_SOUND;
0367:                double numerator = 1.0;
0368:                double denominator = 1.0;
0369:                int direction = NO_CHANGE; // sound movement away or towards listener
0370:
0371:                Point3f lastXformPosition;
0372:                Point3f lastXformCenterEar;
0373:                Point3f xformPosition;
0374:                Point3f xformCenterEar;
0375:                float averagedSoundDistances = 0.0f;
0376:                float averagedEarsDistances = 0.0f;
0377:
0378:                /*
0379:                 *  Average the differences between the last MAX_DISTANCE
0380:                 *  sound positions and head positions
0381:                 */
0382:                if (!averageDistances) {
0383:                    // TODO: Use some EPSilion to do 'equals' test against
0384:                    if (dopplerFlag)
0385:                        debugPrint("JSPositionalSample.calculateDoppler - "
0386:                                + "not enough distance data collected, "
0387:                                + "dopplerRatio set to zero");
0388:                    // can't calculate change in direction
0389:                    return 0.0f; // sample rate ratio is zero
0390:                }
0391:
0392:                lastXformPosition = positions[lastIndex];
0393:                lastXformCenterEar = centerEars[lastIndex];
0394:                xformPosition = positions[currentIndex];
0395:                xformCenterEar = centerEars[currentIndex];
0396:                distanceSourceToHead = xformPosition.distance(xformCenterEar);
0397:                lastDistanceSourceToHead = lastXformPosition
0398:                        .distance(lastXformCenterEar);
0399:                if (dopplerFlag) {
0400:                    debugPrint("JSPositionalSample.calculateDoppler - distances: "
0401:                            + "current,last = "
0402:                            + distanceSourceToHead
0403:                            + ", "
0404:                            + lastDistanceSourceToHead);
0405:                    debugPrint("                                      "
0406:                            + "current position = " + xformPosition.x + ", "
0407:                            + xformPosition.y + ", " + xformPosition.z);
0408:                    debugPrint("                                      "
0409:                            + "current ear = " + xformCenterEar.x + ", "
0410:                            + xformCenterEar.y + ", " + xformCenterEar.z);
0411:                    debugPrint("                                      "
0412:                            + "last position = " + lastXformPosition.x + ", "
0413:                            + lastXformPosition.y + ", " + lastXformPosition.z);
0414:                    debugPrint("                                      "
0415:                            + "last ear = " + lastXformCenterEar.x + ", "
0416:                            + lastXformCenterEar.y + ", "
0417:                            + lastXformCenterEar.z);
0418:                }
0419:                if (distanceSourceToHead == lastDistanceSourceToHead) {
0420:                    // TODO: Use some EPSilion to do 'equals' test against
0421:                    if (dopplerFlag)
0422:                        debugPrint("JSPositionalSample.calculateDoppler - "
0423:                                + "distance diff = 0, dopplerRatio set to zero");
0424:                    // can't calculate change in direction
0425:                    return 0.0f; // sample rate ratio is zero
0426:                }
0427:
0428:                deltaTime = times[currentIndex] - times[firstIndex];
0429:                for (int i = 0; i < (MAX_DISTANCES - 1); i++) {
0430:                    averagedSoundDistances += positions[i + 1]
0431:                            .distance(positions[i]);
0432:                    averagedEarsDistances += centerEars[i + 1]
0433:                            .distance(centerEars[i]);
0434:                }
0435:                averagedSoundDistances /= (MAX_DISTANCES - 1);
0436:                averagedEarsDistances /= (MAX_DISTANCES - 1);
0437:                soundVelocity = averagedSoundDistances / deltaTime;
0438:                headVelocity = averagedEarsDistances / deltaTime;
0439:                if (dopplerFlag) {
0440:                    debugPrint("                                      "
0441:                            + "delta time = " + deltaTime);
0442:                    debugPrint("                                      "
0443:                            + "soundPosition delta = "
0444:                            + xformPosition.distance(lastXformPosition));
0445:                    debugPrint("                                      "
0446:                            + "soundVelocity = " + soundVelocity);
0447:                    debugPrint("                                      "
0448:                            + "headPosition delta = "
0449:                            + xformCenterEar.distance(lastXformCenterEar));
0450:                    debugPrint("                                      "
0451:                            + "headVelocity = " + headVelocity);
0452:                }
0453:                if (attribs != null) {
0454:
0455:                    float rolloff = attribs.rolloff;
0456:                    float velocityScaleFactor = attribs.velocityScaleFactor;
0457:                    if (rolloff != 1.0f) {
0458:                        speedOfSound *= rolloff;
0459:                        if (dopplerFlag)
0460:                            debugPrint("                                      "
0461:                                    + "attrib rollof = " + rolloff);
0462:                    }
0463:                    if (velocityScaleFactor != 1.0f) {
0464:                        soundVelocity *= velocityScaleFactor;
0465:                        headVelocity *= velocityScaleFactor;
0466:                        if (dopplerFlag) {
0467:                            debugPrint("                                      "
0468:                                    + "attrib velocity scale factor = "
0469:                                    + velocityScaleFactor);
0470:                            debugPrint("                                      "
0471:                                    + "new soundVelocity = " + soundVelocity);
0472:                            debugPrint("                                      "
0473:                                    + "new headVelocity = " + headVelocity);
0474:                        }
0475:                    }
0476:                }
0477:                if (distanceSourceToHead < lastDistanceSourceToHead) {
0478:                    // sound and head moving towards each other
0479:                    if (dopplerFlag)
0480:                        debugPrint("                                      "
0481:                                + "moving towards...");
0482:                    direction = TOWARDS;
0483:                    numerator = speedOfSound + headVelocity;
0484:                    denominator = speedOfSound - soundVelocity;
0485:                } else {
0486:                    // sound and head moving away from each other
0487:                    //    note: no change in distance case covered above
0488:                    if (dopplerFlag)
0489:                        debugPrint("                                      "
0490:                                + "moving away...");
0491:                    direction = AWAY;
0492:                    numerator = speedOfSound - headVelocity;
0493:                    denominator = speedOfSound + soundVelocity;
0494:                }
0495:                if (numerator <= 0.0) {
0496:                    if (dopplerFlag)
0497:                        debugPrint("JSPositionalSample.calculateDoppler: "
0498:                                + "BOOM!! - velocity of head > speed of sound");
0499:                    return -1.0f;
0500:                } else if (denominator <= 0.0) {
0501:                    if (dopplerFlag)
0502:                        debugPrint("JSPositionalSample.calculateDoppler: "
0503:                                + "BOOM!! - velocity of sound source negative");
0504:                    return -1.0f;
0505:                } else {
0506:                    if (dopplerFlag)
0507:                        debugPrint("JSPositionalSample.calculateDoppler: "
0508:                                + "numerator = " + numerator
0509:                                + ", denominator = " + denominator);
0510:                    sampleRateRatio = numerator / denominator;
0511:                }
0512:
0513:                /********
0514:                 IF direction WERE important to calling method...
0515:                 * Return value greater than 0 denotes direction of sound source is
0516:                 *        towards the listener
0517:                 * Return value less than 0 denotes direction of sound source is
0518:                 *        away from the listener
0519:                 if (direction == AWAY)
0520:                 return -((float)sampleRateRatio);
0521:                 else
0522:                 return (float)sampleRateRatio;
0523:                 *********/
0524:                return (float) sampleRateRatio;
0525:            }
0526:
0527:            void updateEar(int dirtyFlags, View view) {
0528:                if (debugFlag)
0529:                    debugPrint("*** updateEar fields");
0530:                // xform Ear
0531:                Point3f xformCenterEar = new Point3f();
0532:                if (!calculateNewEar(dirtyFlags, view, xformCenterEar)) {
0533:                    if (debugFlag)
0534:                        debugPrint("calculateNewEar returned false");
0535:                    return;
0536:                }
0537:                // store ear and increment indices ONLY if there is an actual change
0538:                if (xformCenterEar.x == centerEars[currentIndex].x
0539:                        && xformCenterEar.y == centerEars[currentIndex].y
0540:                        && xformCenterEar.z == centerEars[currentIndex].z) {
0541:                    if (debugFlag)
0542:                        debugPrint("    No change in ear, so don't reset");
0543:                    return;
0544:                }
0545:                // store xform Ear 
0546:                incrementIndices();
0547:                times[currentIndex] = System.currentTimeMillis();
0548:                centerEars[currentIndex].set(xformCenterEar);
0549:                // since this is a change to the head position and not the sound
0550:                // position save the last sound position into the current element
0551:                if (numDistances > 1)
0552:                    positions[currentIndex].set(positions[lastIndex]);
0553:            }
0554:
0555:            boolean calculateNewEar(int dirtyFlags, View view,
0556:                    Point3f xformCenterEar) {
0557:                /*
0558:                 * Transform ear position (from Head) into Virtual World Coord space
0559:                 */
0560:                Point3d earPosition = new Point3d(); // temporary double Point
0561:
0562:                // TODO: check dirty flags coming in
0563:                //     For now, recalculate ear positions by forcing earsXformed false
0564:                boolean earsXformed = false;
0565:                if (!earsXformed) {
0566:                    if (view != null) {
0567:                        PhysicalBody body = view.getPhysicalBody();
0568:                        if (body != null) {
0569:
0570:                            // Get Head Coord. to Virtual World transform
0571:                            // TODO: re-enable this when userHeadToVworld is 
0572:                            //     implemented correctly!!!
0573:                            Transform3D headToVwrld = new Transform3D();
0574:                            view.getUserHeadToVworld(headToVwrld);
0575:                            if (debugFlag) {
0576:                                debugPrint("user head to Vwrld colum-major:");
0577:                                double[] matrix = new double[16];
0578:                                headToVwrld.get(matrix);
0579:                                debugPrint("JSPosSample    " + matrix[0] + ", "
0580:                                        + matrix[1] + ", " + matrix[2] + ", "
0581:                                        + matrix[3]);
0582:                                debugPrint("JSPosSample    " + matrix[4] + ", "
0583:                                        + matrix[5] + ", " + matrix[6] + ", "
0584:                                        + matrix[7]);
0585:                                debugPrint("JSPosSample    " + matrix[8] + ", "
0586:                                        + matrix[9] + ", " + matrix[10] + ", "
0587:                                        + matrix[11]);
0588:                                debugPrint("JSPosSample    " + matrix[12]
0589:                                        + ", " + matrix[13] + ", " + matrix[14]
0590:                                        + ", " + matrix[15]);
0591:                            }
0592:
0593:                            // Get left and right ear positions in Head Coord.s
0594:                            // Transforms left and right ears to Virtual World coord.s
0595:                            body.getLeftEarPosition(earPosition);
0596:                            xformLeftEar.x = (float) earPosition.x;
0597:                            xformLeftEar.y = (float) earPosition.y;
0598:                            xformLeftEar.z = (float) earPosition.z;
0599:                            body.getRightEarPosition(earPosition);
0600:                            xformRightEar.x = (float) earPosition.x;
0601:                            xformRightEar.y = (float) earPosition.y;
0602:                            xformRightEar.z = (float) earPosition.z;
0603:                            headToVwrld.transform(xformRightEar);
0604:                            headToVwrld.transform(xformLeftEar);
0605:                            // Transform head viewing (Z) axis to Virtual World coord.s
0606:                            xformHeadZAxis.set(0.0f, 0.0f, -1.0f); // Va
0607:                            headToVwrld.transform(xformHeadZAxis);
0608:
0609:                            // calculate the new (current) mid-point between the ears
0610:                            // find the mid point between left and right ear positions
0611:                            xformCenterEar.x = xformLeftEar.x
0612:                                    + ((xformRightEar.x - xformLeftEar.x) * 0.5f);
0613:                            xformCenterEar.y = xformLeftEar.y
0614:                                    + ((xformRightEar.y - xformLeftEar.y) * 0.5f);
0615:                            xformCenterEar.z = xformLeftEar.z
0616:                                    + ((xformRightEar.z - xformLeftEar.z) * 0.5f);
0617:                            // TODO: when head changes earDirty should be set!
0618:                            // earDirty = false;
0619:                            if (debugFlag) {
0620:                                debugPrint("           earXformed CALCULATED");
0621:                                debugPrint("           xformCenterEar = "
0622:                                        + xformCenterEar.x + " "
0623:                                        + xformCenterEar.y + " "
0624:                                        + xformCenterEar.z);
0625:                            }
0626:                            earsXformed = true;
0627:                        } // end of body NOT null
0628:                    } // end of view NOT null
0629:                } // end of earsDirty
0630:                else {
0631:                    // TODO:  use existing transformed ear positions
0632:                }
0633:
0634:                if (!earsXformed) {
0635:                    // uses the default head position of (0.0, -0.03, 0.095)
0636:                    if (debugFlag)
0637:                        debugPrint("           earXformed NOT calculated");
0638:                }
0639:                return earsXformed;
0640:            }
0641:
0642:            /**  
0643:             * Render this sample
0644:             *
0645:             * Calculate the audiodevice parameters necessary to spatially play this
0646:             * sound.
0647:             */
0648:            public void render(int dirtyFlags, View view,
0649:                    AuralParameters attribs) {
0650:                if (debugFlag)
0651:                    debugPrint("JSPositionalSample.render");
0652:                updateEar(dirtyFlags, view);
0653:
0654:                /*
0655:                 * Time to check velocities and change the playback rate if necessary...
0656:                 *
0657:                 * Rolloff value MUST be > zero for any sound to be heard!
0658:                 *     If rolloff is zero, all sounds affected by AuralAttribute region
0659:                 *     are silent.
0660:                 * FrequencyScaleFactor value MUST be > zero for any sound to be heard!
0661:                 *     since Sfreq' = Sfreq * frequencyScaleFactor.
0662:                 *     If FrequencyScaleFactor is zero, all sounds affected by 
0663:                 *     AuralAttribute region are paused.
0664:                 * VelocityScaleFactor value of zero disables Doppler calculations.
0665:                 *
0666:                 * Scale 'Doppler' rate (or lack of Doppler) by frequencyScaleFactor.
0667:                 */
0668:                float dopplerRatio = 1.0f;
0669:                if (attribs != null) {
0670:                    float rolloff = attribs.rolloff;
0671:                    float frequencyScaleFactor = attribs.frequencyScaleFactor;
0672:                    float velocityScaleFactor = attribs.velocityScaleFactor;
0673:                    if (debugFlag || dopplerFlag)
0674:                        debugPrint("JSPositionalSample: attribs NOT null");
0675:                    if (rolloff <= 0.0f) {
0676:                        if (debugFlag)
0677:                            debugPrint("    rolloff = " + rolloff + " <= 0.0");
0678:                        // TODO: Make sound silent
0679:                        // return ???
0680:                    } else if (frequencyScaleFactor <= 0.0f) {
0681:                        if (debugFlag)
0682:                            debugPrint("    freqScaleFactor = "
0683:                                    + frequencyScaleFactor + " <= 0.0");
0684:                        // TODO: Pause sound silent
0685:                        // return ???
0686:                    } else if (velocityScaleFactor > 0.0f) {
0687:                        if (debugFlag || dopplerFlag)
0688:                            debugPrint("    velocityScaleFactor = "
0689:                                    + velocityScaleFactor);
0690:                        /*******
0691:                         if (deltaTime > 0) {
0692:                         *******/
0693:                        // Doppler can be calculated after the second time
0694:                        // updateXformParams() is executed
0695:                        dopplerRatio = calculateDoppler(attribs);
0696:
0697:                        if (dopplerRatio == 0.0f) {
0698:                            // dopplerRatio zeroo denotes no changed
0699:                            // TODO: But what if frequencyScaleFactor has changed
0700:                            if (debugFlag) {
0701:                                debugPrint("JSPositionalSample: render: "
0702:                                        + "dopplerRatio returned zero; no change");
0703:                            }
0704:                        } else if (dopplerRatio == -1.0f) {
0705:                            // error returned by calculateDoppler
0706:                            if (debugFlag) {
0707:                                debugPrint("JSPositionalSample: render: "
0708:                                        + "dopplerRatio returned = "
0709:                                        + dopplerRatio + "< 0");
0710:                            }
0711:                            // TODO: Make sound silent
0712:                            // return ???
0713:                        } else if (dopplerRatio > 0.0f) {
0714:                            // rate could be changed
0715:                            rateRatio = dopplerRatio * frequencyScaleFactor
0716:                                    * getRateScaleFactor();
0717:                            if (debugFlag) {
0718:                                debugPrint("    scaled by frequencyScaleFactor = "
0719:                                        + frequencyScaleFactor);
0720:                            }
0721:                        }
0722:                        /******
0723:                         }
0724:                         else  {
0725:                         if (debugFlag)
0726:                         debugPrint("deltaTime <= 0 - skip Doppler calc");
0727:                         }
0728:                         ******/
0729:                    } else { // auralAttributes not null but velocityFactor <= 0
0730:                        // Doppler is disabled
0731:                        rateRatio = frequencyScaleFactor * getRateScaleFactor();
0732:                    }
0733:                }
0734:                /* 
0735:                 * since aural attributes undefined, default values are used,
0736:                 *   thus no Doppler calculated
0737:                 */
0738:                else {
0739:                    if (debugFlag || dopplerFlag)
0740:                        debugPrint("JSPositionalSample: attribs null");
0741:                    rateRatio = 1.0f;
0742:                }
0743:
0744:                this .panSample(attribs);
0745:            }
0746:
0747:            /* *****************
0748:             *   
0749:             *  Calculate Angular Gain
0750:             *   
0751:             * *****************/
0752:            /*   
0753:             *  Calculates the Gain scale factor applied to the overall gain for
0754:             *  a sound based on angle between a sound's projected direction and the
0755:             *  vector between the sounds position and center ear.
0756:             *
0757:             *  For Point Sounds this value is always 1.0f.
0758:             */
0759:            float calculateAngularGain() {
0760:                return (1.0f);
0761:            }
0762:
0763:            /* *****************
0764:             *   
0765:             *  Calculate Filter
0766:             *   
0767:             * *****************/
0768:            /*   
0769:             *  Calculates the low-pass cutoff frequency filter value applied to the 
0770:             *  a sound based on both:
0771:             *      Distance Filter (from Aural Attributes) based on distance
0772:             *         between the sound and the listeners position
0773:             *      Angular Filter (for Directional Sounds) based on the angle
0774:             *         between a sound's projected direction and the
0775:             *         vector between the sounds position and center ear.
0776:             *  The lowest of these two filter is used.
0777:             *  This filter value is stored into the sample's filterFreq field.
0778:             */
0779:            void calculateFilter(float distance, AuralParameters attribs) {
0780:                // setting filter cutoff freq to 44.1kHz which, in this
0781:                // implementation, is the same as not performing filtering
0782:                float distanceFilter = 44100.0f;
0783:                float angularFilter = 44100.0f;
0784:                int arrayLength = attribs.getDistanceFilterLength();
0785:                int filterType = attribs.getDistanceFilterType();
0786:                boolean distanceFilterFound = false;
0787:                boolean angularFilterFound = false;
0788:                if ((filterType != AuralParameters.NO_FILTERING)
0789:                        && arrayLength > 0) {
0790:                    double[] distanceArray = new double[arrayLength];
0791:                    float[] cutoffArray = new float[arrayLength];
0792:                    attribs.getDistanceFilter(distanceArray, cutoffArray);
0793:
0794:                    if (debugFlag) {
0795:                        debugPrint("distanceArray    cutoffArray");
0796:                        for (int i = 0; i < arrayLength; i++)
0797:                            debugPrint((float) (distanceArray[i]) + ", "
0798:                                    + cutoffArray[i]);
0799:                    }
0800:                    distanceFilter = findFactor((double) distance,
0801:                            distanceArray, cutoffArray);
0802:                    if (distanceFilter < 0.0f)
0803:                        distanceFilterFound = false;
0804:                    else
0805:                        distanceFilterFound = true;
0806:                } else {
0807:                    distanceFilterFound = false;
0808:                    distanceFilter = -1.0f;
0809:                }
0810:
0811:                if (debugFlag)
0812:                    debugPrint("    calculateFilter arrayLength = "
0813:                            + arrayLength);
0814:
0815:                // Angular filter only applies to directional sound sources.
0816:                angularFilterFound = false;
0817:                angularFilter = -1.0f;
0818:
0819:                filterFlag = distanceFilterFound || angularFilterFound;
0820:                filterFreq = distanceFilter;
0821:                if (debugFlag)
0822:                    debugPrint("    calculateFilter flag,freq = " + filterFlag
0823:                            + "," + filterFreq);
0824:            }
0825:
0826:            /* *****************
0827:             *   
0828:             *  Find Factor
0829:             *   
0830:             * *****************/
0831:            /*
0832:             *  Interpolates the correct output factor given a 'distance' value
0833:             *  and references to the distance array and factor array used in
0834:             *  the calculation.  These array parameters could be either linear or
0835:             *  angular distance arrays, or filter arrays.
0836:             *  The values in the distance array are monotonically increasing.
0837:             *  This method looks at pairs of distance array values to find which
0838:             *  pair the input distance argument is between distanceArray[index] and
0839:             *  distanceArray[index+1].
0840:             *  The index is used to get factorArray[index] and factorArray[index+1].
0841:             *  Then the ratio of the 'distance' between this pair of distanceArray 
0842:             *  values is used to scale the two found factorArray values proportionally.
0843:             *  The resulting factor is returned, unless there is an error, then -1.0
0844:             *  is returned.
0845:             */
0846:            float findFactor(double distance, double[] distanceArray,
0847:                    float[] factorArray) {
0848:                int index, lowIndex, highIndex, indexMid;
0849:
0850:                if (debugFlag)
0851:                    debugPrint("JSPositionalSample.findFactor entered");
0852:
0853:                /*
0854:                 * Error checking
0855:                 */
0856:                if (distanceArray == null || factorArray == null) {
0857:                    if (debugFlag)
0858:                        debugPrint("   findFactor: arrays null");
0859:                    return -1.0f; // no value
0860:                }
0861:                int arrayLength = distanceArray.length;
0862:                if (arrayLength < 2) {
0863:                    if (debugFlag)
0864:                        debugPrint("   findFactor: arrays length < 2");
0865:                    return -1.0f; // no value
0866:                }
0867:                int largestIndex = arrayLength - 1;
0868:
0869:                /*
0870:                 * Calculate distanceGain scale factor
0871:                 */
0872:                if (distance >= distanceArray[largestIndex]) {
0873:                    if (debugFlag) {
0874:                        debugPrint("   findFactor: distance > "
0875:                                + distanceArray[largestIndex]);
0876:                        debugPrint("   distanceArray length = " + arrayLength);
0877:                    }
0878:                    return factorArray[largestIndex];
0879:                } else if (distance <= distanceArray[0]) {
0880:                    if (debugFlag)
0881:                        debugPrint("   findFactor: distance < "
0882:                                + distanceArray[0]);
0883:                    return factorArray[0];
0884:                }
0885:                /*
0886:                 * Distance between points within attenuation array.
0887:                 * Use binary halfing of distance array
0888:                 */
0889:                else {
0890:                    lowIndex = 0;
0891:                    highIndex = largestIndex;
0892:                    if (debugFlag)
0893:                        debugPrint("   while loop to find index: ");
0894:                    while (lowIndex < (highIndex - 1)) {
0895:                        if (debugFlag) {
0896:                            debugPrint("       lowIndex " + lowIndex
0897:                                    + ", highIndex " + highIndex);
0898:                            debugPrint("       d.A. pair for lowIndex "
0899:                                    + distanceArray[lowIndex] + ", "
0900:                                    + factorArray[lowIndex]);
0901:                            debugPrint("       d.A. pair for highIndex "
0902:                                    + distanceArray[highIndex] + ", "
0903:                                    + factorArray[highIndex]);
0904:                        }
0905:                        /*
0906:                         * we can assume distance is between distance atttenuation vals
0907:                         * distanceArray[lowIndex] and distanceArray[highIndex]
0908:                         * calculate gain scale factor based on distance
0909:                         */
0910:                        if (distanceArray[lowIndex] >= distance) {
0911:                            if (distance < distanceArray[lowIndex]) {
0912:                                if (internalErrors)
0913:                                    debugPrint("Internal Error: binary halving in "
0914:                                            + " findFactor failed; distance < index value");
0915:                            }
0916:                            if (debugFlag) {
0917:                                debugPrint("       index == distanceGain "
0918:                                        + lowIndex);
0919:                                debugPrint("        findFactor returns [LOW="
0920:                                        + lowIndex + "] "
0921:                                        + factorArray[lowIndex]);
0922:                            }
0923:                            // take value of scale factor directly from factorArray
0924:                            return factorArray[lowIndex];
0925:                        } else if (distanceArray[highIndex] <= distance) {
0926:                            if (distance > distanceArray[highIndex]) {
0927:                                if (internalErrors)
0928:                                    debugPrint("Internal Error: binary halving in "
0929:                                            + " findFactor failed; distance > index value");
0930:                            }
0931:                            if (debugFlag) {
0932:                                debugPrint("       index == distanceGain "
0933:                                        + highIndex);
0934:                                debugPrint("        findFactor returns [HIGH="
0935:                                        + highIndex + "] "
0936:                                        + factorArray[highIndex]);
0937:                            }
0938:                            // take value of scale factor directly from factorArray
0939:                            return factorArray[highIndex];
0940:                        }
0941:                        if (distance > distanceArray[lowIndex]
0942:                                && distance < distanceArray[highIndex]) {
0943:                            indexMid = lowIndex + ((highIndex - lowIndex) / 2);
0944:                            if (distance <= distanceArray[indexMid])
0945:                                // value of distance in lower "half" of list
0946:                                highIndex = indexMid;
0947:                            else
0948:                                // value if distance in upper "half" of list
0949:                                lowIndex = indexMid;
0950:                        }
0951:                    } /* of while */
0952:
0953:                    /*
0954:                     * ratio: distance from listener to sound source 
0955:                     *        between lowIndex and highIndex times
0956:                     *        attenuation value between lowIndex and highIndex
0957:                     * gives linearly interpolationed attenuation value
0958:                     */
0959:                    if (debugFlag) {
0960:                        debugPrint("   ratio calculated using lowIndex "
0961:                                + lowIndex + ", highIndex " + highIndex);
0962:                        debugPrint("   d.A. pair for lowIndex "
0963:                                + distanceArray[lowIndex] + ", "
0964:                                + factorArray[lowIndex]);
0965:                        debugPrint("   d.A. pair for highIndex "
0966:                                + distanceArray[highIndex] + ", "
0967:                                + factorArray[highIndex]);
0968:                    }
0969:
0970:                    float outputFactor = ((float) (((distance - distanceArray[lowIndex]) / (distanceArray[highIndex] - distanceArray[lowIndex]))) * (factorArray[highIndex] - factorArray[lowIndex]))
0971:                            + factorArray[lowIndex];
0972:                    if (debugFlag)
0973:                        debugPrint("    findFactor returns " + outputFactor);
0974:                    return outputFactor;
0975:                }
0976:            }
0977:
0978:            /**
0979:             * CalculateDistanceAttenuation
0980:             *
0981:             * Simply calls generic (for PointSound) 'findFactor()' with 
0982:             * a single set of attenuation distance and gain scale factor arrays.
0983:             */
0984:            float calculateDistanceAttenuation(float distance) {
0985:                float factor = 1.0f;
0986:                factor = findFactor((double) distance,
0987:                        this .attenuationDistance, this .attenuationGain);
0988:                if (factor >= 0.0)
0989:                    return (factor);
0990:                else
0991:                    return (1.0f);
0992:            }
0993:
0994:            /* ******************
0995:             *   
0996:             *  Pan Sample
0997:             *   
0998:             * ******************/
0999:            /*  
1000:             *  Sets pan and delay for a single sample associated with this Sound.
1001:             *  Front and Back quadrants are treated the same.
1002:             */
1003:            void panSample(AuralParameters attribs) {
1004:                int quadrant = 1;
1005:                float intensityHigh = 1.0f;
1006:                float intensityLow = 0.125f;
1007:                float intensityDifference = intensityHigh - intensityLow;
1008:
1009:                //TODO: time around "average" default head
1010:                // int     delayHigh = 32;  // 32.15 samples = .731 ms
1011:                // int     delayLow = 0;
1012:
1013:                float intensityOffset; // 0.0 -> 1.0 then 1.0 -> 0.0 for full rotation
1014:                float halfX;
1015:                int id;
1016:                int err;
1017:
1018:                float nearZero = 0.000001f;
1019:                float nearOne = 0.999999f;
1020:                float nearNegativeOne = -nearOne;
1021:                float halfPi = (float) Math.PI * 0.5f;
1022:                /*
1023:                 * Parameters used for IID and ITD equations.
1024:                 * Name of parameters (as used in Guide, E.3) are denoted in comments.
1025:                 */
1026:                float distanceSourceToCenterEar = 0.0f; // Dh
1027:                float lastDistanceSourceToCenterEar = 0.0f;
1028:                float distanceSourceToRightEar = 0.0f; // Ef or Ec
1029:                float distanceSourceToLeftEar = 0.0f; // Ef or Ec
1030:                float distanceBetweenEars = 0.18f; // De
1031:                float radiusOfHead = 0.0f; // De/2
1032:                float radiusOverDistanceToSource = 0.0f; // De/2 * 1/Dh
1033:
1034:                float alpha = 0.0f; // 'alpha'
1035:                float sinAlpha = 0.0f; // sin(alpha);
1036:                float gamma = 0.0f; // 'gamma'
1037:
1038:                // Speed of Sound (unaffected by rolloff) in millisec/meters
1039:                float speedOfSound = attribs.SPEED_OF_SOUND;
1040:                float invSpeedOfSound = 1.0f / attribs.SPEED_OF_SOUND;
1041:
1042:                float sampleRate = 44.1f; // 44 samples/millisec
1043:
1044:                boolean rightEarClosest = false;
1045:                boolean soundFromBehind = false;
1046:
1047:                float distanceGain = 1.0f;
1048:                float allGains = this .gain; // product of gain scale factors
1049:
1050:                Point3f workingPosition = new Point3f();
1051:                Point3f workingCenterEar = new Point3f();
1052:
1053:                // Asuumes that head and ear positions can be retrieved from universe
1054:
1055:                Vector3f mixScale = new Vector3f(); // for mix*Samples code
1056:
1057:                // Use transformed position of this sound
1058:                workingPosition.set(positions[currentIndex]);
1059:                workingCenterEar.set(centerEars[currentIndex]);
1060:                if (debugFlag) {
1061:                    debugPrint("panSample:workingPosition from" + " positions["
1062:                            + currentIndex + "] -> " + workingPosition.x + ", "
1063:                            + workingPosition.y + ", " + workingPosition.z
1064:                            + " for pointSound " + this );
1065:                    debugPrint("panSample:workingCenterEar "
1066:                            + workingCenterEar.x + " " + workingCenterEar.y
1067:                            + " " + workingCenterEar.z);
1068:                    debugPrint("panSample:xformLeftEar " + xformLeftEar.x + " "
1069:                            + xformLeftEar.y + " " + xformLeftEar.z);
1070:                    debugPrint("panSample:xformRightEar " + xformRightEar.x
1071:                            + " " + xformRightEar.y + " " + xformRightEar.z);
1072:                }
1073:
1074:                // Create the vectors from the sound source to head positions
1075:                sourceToCenterEar.x = workingCenterEar.x - workingPosition.x;
1076:                sourceToCenterEar.y = workingCenterEar.y - workingPosition.y;
1077:                sourceToCenterEar.z = workingCenterEar.z - workingPosition.z;
1078:                sourceToRightEar.x = xformRightEar.x - workingPosition.x;
1079:                sourceToRightEar.y = xformRightEar.y - workingPosition.y;
1080:                sourceToRightEar.z = xformRightEar.z - workingPosition.z;
1081:                sourceToLeftEar.x = xformLeftEar.x - workingPosition.x;
1082:                sourceToLeftEar.y = xformLeftEar.y - workingPosition.y;
1083:                sourceToLeftEar.z = xformLeftEar.z - workingPosition.z;
1084:
1085:                /*
1086:                 * get distances from SoundSource to 
1087:                 *    (i)   head origin
1088:                 *    (ii)  right ear
1089:                 *    (iii) left ear
1090:                 */
1091:                distanceSourceToCenterEar = workingPosition
1092:                        .distance(workingCenterEar);
1093:                distanceSourceToRightEar = workingPosition
1094:                        .distance(xformRightEar);
1095:                distanceSourceToLeftEar = workingPosition
1096:                        .distance(xformLeftEar);
1097:                distanceBetweenEars = xformRightEar.distance(xformLeftEar);
1098:                if (debugFlag)
1099:                    debugPrint("           distance from left,right ears to source: = ("
1100:                            + distanceSourceToLeftEar
1101:                            + ", "
1102:                            + distanceSourceToRightEar + ")");
1103:
1104:                radiusOfHead = distanceBetweenEars * 0.5f;
1105:                if (debugFlag)
1106:                    debugPrint("           radius of head = " + radiusOfHead);
1107:                radiusOverDistanceToSource = // De/2 * 1/Dh
1108:                radiusOfHead / distanceSourceToCenterEar;
1109:                if (debugFlag)
1110:                    debugPrint("           radius over distance = "
1111:                            + radiusOverDistanceToSource);
1112:                if (debugFlag) {
1113:                    debugPrint("panSample:source to center ear "
1114:                            + sourceToCenterEar.x + " " + sourceToCenterEar.y
1115:                            + " " + sourceToCenterEar.z);
1116:                    debugPrint("panSample:xform'd Head ZAxis "
1117:                            + xformHeadZAxis.x + " " + xformHeadZAxis.y + " "
1118:                            + xformHeadZAxis.z);
1119:                    debugPrint("panSample:length of sourceToCenterEar "
1120:                            + sourceToCenterEar.length());
1121:                    debugPrint("panSample:length of xformHeadZAxis "
1122:                            + xformHeadZAxis.length());
1123:                }
1124:
1125:                // Dot Product 
1126:                double dotProduct = (double) ((sourceToCenterEar
1127:                        .dot(xformHeadZAxis)) / (sourceToCenterEar.length() * xformHeadZAxis
1128:                        .length()));
1129:                if (debugFlag)
1130:                    debugPrint("           dot product = " + dotProduct);
1131:                alpha = (float) (Math.acos(dotProduct));
1132:                if (debugFlag)
1133:                    debugPrint("           alpha = " + alpha);
1134:
1135:                if (alpha > halfPi) {
1136:                    if (debugFlag)
1137:                        debugPrint("           sound from behind");
1138:                    soundFromBehind = true;
1139:                    alpha = (float) Math.PI - alpha;
1140:                    if (debugFlag)
1141:                        debugPrint("           PI minus alpha =>" + alpha);
1142:                } else {
1143:                    soundFromBehind = false;
1144:                    if (debugFlag)
1145:                        debugPrint("           sound from in front");
1146:                }
1147:
1148:                gamma = (float) (Math.acos(radiusOverDistanceToSource));
1149:                if (debugFlag)
1150:                    debugPrint("           gamma " + gamma);
1151:
1152:                rightEarClosest = (distanceSourceToRightEar > distanceSourceToLeftEar) ? false
1153:                        : true;
1154:                /*
1155:                 * Determine the quadrant sound is in 
1156:                 */
1157:                if (rightEarClosest) {
1158:                    if (debugFlag)
1159:                        debugPrint("           right ear closest");
1160:                    if (soundFromBehind)
1161:                        quadrant = 4;
1162:                    else
1163:                        quadrant = 1;
1164:                } else {
1165:                    if (debugFlag)
1166:                        debugPrint("           left ear closest");
1167:                    if (soundFromBehind)
1168:                        quadrant = 3;
1169:                    else
1170:                        quadrant = 2;
1171:                }
1172:                sinAlpha = (float) (Math.sin((double) alpha));
1173:                if (sinAlpha < 0.0)
1174:                    sinAlpha = -sinAlpha;
1175:                if (debugFlag)
1176:                    debugPrint("           sin(alpha) " + sinAlpha);
1177:
1178:                /*
1179:                 * The path from sound source to the farthest ear is always indirect
1180:                 * (it wraps around part of the head).
1181:                 * Calculate distance wrapped around the head for farthest ear
1182:                 */
1183:                float DISTANCE = (float) Math
1184:                        .sqrt((double) distanceSourceToCenterEar
1185:                                * distanceSourceToCenterEar + radiusOfHead
1186:                                * radiusOfHead);
1187:                if (debugFlag)
1188:                    debugPrint("           partial distance from edge of head to source = "
1189:                            + distanceSourceToCenterEar);
1190:                if (rightEarClosest) {
1191:                    distanceSourceToLeftEar = DISTANCE + radiusOfHead
1192:                            * (halfPi + alpha - gamma);
1193:                    if (debugFlag)
1194:                        debugPrint("           new distance from left ear to source = "
1195:                                + distanceSourceToLeftEar);
1196:                } else {
1197:                    distanceSourceToRightEar = DISTANCE + radiusOfHead
1198:                            * (halfPi + alpha - gamma);
1199:                    if (debugFlag)
1200:                        debugPrint("           new distance from right ear to source = "
1201:                                + distanceSourceToRightEar);
1202:                }
1203:                /*
1204:                 * The path from the source source to the closest ear could either
1205:                 * be direct or indirect (wraps around part of the head).
1206:                 * if sinAlpha >= radiusOverDistance path of sound to closest ear
1207:                 * is direct, otherwise it is indirect
1208:                 */
1209:                if (sinAlpha < radiusOverDistanceToSource) {
1210:                    if (debugFlag)
1211:                        debugPrint("           closest path is also indirect ");
1212:                    // Path of sound to closest ear is indirect
1213:
1214:                    if (rightEarClosest) {
1215:                        distanceSourceToRightEar = DISTANCE + radiusOfHead
1216:                                * (halfPi - alpha - gamma);
1217:                        if (debugFlag)
1218:                            debugPrint("           new distance from right ear to source = "
1219:                                    + distanceSourceToRightEar);
1220:                    } else {
1221:                        distanceSourceToLeftEar = DISTANCE + radiusOfHead
1222:                                * (halfPi - alpha - gamma);
1223:                        if (debugFlag)
1224:                            debugPrint("           new distance from left ear to source = "
1225:                                    + distanceSourceToLeftEar);
1226:                    }
1227:                } else {
1228:                    if (debugFlag)
1229:                        debugPrint("           closest path is direct ");
1230:                    if (rightEarClosest) {
1231:                        if (debugFlag)
1232:                            debugPrint("           direct distance from right ear to source = "
1233:                                    + distanceSourceToRightEar);
1234:                    } else {
1235:                        if (debugFlag)
1236:                            debugPrint("           direct distance from left ear to source = "
1237:                                    + distanceSourceToLeftEar);
1238:                    }
1239:                }
1240:
1241:                /**
1242:                 * Short-cut taken.  Rather than using actual delays from source
1243:                 * (where the overall distances would be taken into account in 
1244:                 * determining delay) the difference in the left and right delay
1245:                 * are applied.
1246:                 * This approach will be preceptibly wrong for sound sources that
1247:                 * are very far away from the listener so both ears would have 
1248:                 * large delay.
1249:                 */
1250:                sampleRate = channel.rateInHz * (0.001f); // rate in milliseconds
1251:                if (rightEarClosest) {
1252:                    rightDelay = 0;
1253:                    leftDelay = (int) ((distanceSourceToLeftEar - distanceSourceToRightEar)
1254:                            * invSpeedOfSound * sampleRate);
1255:                } else {
1256:                    leftDelay = 0;
1257:                    rightDelay = (int) ((distanceSourceToRightEar - distanceSourceToLeftEar)
1258:                            * invSpeedOfSound * sampleRate);
1259:                }
1260:
1261:                if (debugFlag) {
1262:                    debugPrint("           using inverted SoS = "
1263:                            + invSpeedOfSound);
1264:                    debugPrint("           and sample rate = " + sampleRate);
1265:                    debugPrint("           left and right delay = ("
1266:                            + leftDelay + ", " + rightDelay + ")");
1267:                }
1268:
1269:                // What should the gain be for the different ears???
1270:                // TODO: now using a hack that sets gain based on a unit circle!!!
1271:                workingPosition.sub(workingCenterEar); // offset sound pos. by head origin
1272:                // normalize; put Sound on unit sphere around head origin
1273:                workingPosition.scale(1.0f / distanceSourceToCenterEar);
1274:                if (debugFlag)
1275:                    debugPrint("           workingPosition after unitization "
1276:                            + workingPosition.x + " " + workingPosition.y + " "
1277:                            + workingPosition.z);
1278:
1279:                /*
1280:                 * Get the correct distance gain scale factor from attenuation arrays.
1281:                 * This requires that sourceToCenterEar vector has been calculated.
1282:                 */
1283:                // TODO: now using distance from center ear to source
1284:                //    Using distances from each ear to source would be more accurate
1285:                distanceGain = calculateDistanceAttenuation(distanceSourceToCenterEar);
1286:
1287:                allGains *= distanceGain;
1288:
1289:                /*
1290:                 * Add angular gain (for Cone sound)
1291:                 */
1292:                if (debugFlag)
1293:                    debugPrint("           all Gains (without angular gain) "
1294:                            + allGains);
1295:                // assume that transfromed Position is already calculated
1296:                allGains *= this .calculateAngularGain();
1297:                if (debugFlag)
1298:                    debugPrint("                     (incl. angular gain)  "
1299:                            + allGains);
1300:
1301:                halfX = workingPosition.x / 2.0f;
1302:                if (halfX >= 0)
1303:                    intensityOffset = (intensityDifference * (0.5f - halfX));
1304:                else
1305:                    intensityOffset = (intensityDifference * (0.5f + halfX));
1306:
1307:                /*
1308:                 * For now have delay constant for front back sound for now
1309:                 */
1310:                if (debugFlag)
1311:                    debugPrint("panSample:                 quadrant "
1312:                            + quadrant);
1313:                switch (quadrant) {
1314:                case 1:
1315:                    // Sound from front, right of center of head
1316:                case 4:
1317:                    // Sound from back, right of center of head
1318:                    rightGain = allGains * (intensityHigh - intensityOffset);
1319:                    leftGain = allGains * (intensityLow + intensityOffset);
1320:                    break;
1321:
1322:                case 2:
1323:                    // Sound from front, left of center of head
1324:                case 3:
1325:                    // Sound from back, right of center of head
1326:                    leftGain = allGains * (intensityHigh - intensityOffset);
1327:                    rightGain = allGains * (intensityLow + intensityOffset);
1328:                    break;
1329:                } /* switch */
1330:                if (debugFlag)
1331:                    debugPrint("panSample:                 left/rightGain "
1332:                            + leftGain + ", " + rightGain);
1333:
1334:                // Combines distance and angular filter to set this sample's current
1335:                // frequency cutoff value
1336:                calculateFilter(distanceSourceToCenterEar, attribs);
1337:
1338:            } /* panSample() */
1339:
1340:            // NOTE: setGain in audioengines.Sample is used to set/get user suppled factor
1341:            //     this class uses this single gain value to calculate the left and
1342:            //     right gain values
1343:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.