Skip to content

Commit 3e24208

Browse files
committed
repo move
0 parents  commit 3e24208

File tree

11 files changed

+665
-0
lines changed

11 files changed

+665
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.env
2+
target/

dependency-reduced-pom.xml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<groupId>com.tadghwagstaff.pprrank</groupId>
5+
<artifactId>pprrank</artifactId>
6+
<version>0.1.0</version>
7+
<build>
8+
<plugins>
9+
<plugin>
10+
<artifactId>maven-shade-plugin</artifactId>
11+
<version>3.2.4</version>
12+
<executions>
13+
<execution>
14+
<phase>package</phase>
15+
<goals>
16+
<goal>shade</goal>
17+
</goals>
18+
<configuration>
19+
<transformers>
20+
<transformer>
21+
<mainClass>main.java.pprrankserver.PprRankServer</mainClass>
22+
</transformer>
23+
</transformers>
24+
</configuration>
25+
</execution>
26+
</executions>
27+
</plugin>
28+
</plugins>
29+
</build>
30+
<properties>
31+
<maven.compiler.target>1.8</maven.compiler.target>
32+
<maven.compiler.source>1.8</maven.compiler.source>
33+
</properties>
34+
</project>

pom.xml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<groupId>com.tadghwagstaff.pprrank</groupId>
7+
<artifactId>pprrank</artifactId>
8+
<packaging>jar</packaging>
9+
<version>0.1.0</version>
10+
11+
<properties>
12+
<maven.compiler.source>1.8</maven.compiler.source>
13+
<maven.compiler.target>1.8</maven.compiler.target>
14+
</properties>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.mongodb</groupId>
19+
<artifactId>mongodb-driver-sync</artifactId>
20+
<version>4.7.2</version>
21+
</dependency>
22+
<dependency>
23+
<groupId>io.github.cdimascio</groupId>
24+
<artifactId>dotenv-java</artifactId>
25+
<version>2.2.4</version>
26+
</dependency>
27+
</dependencies>
28+
29+
<build>
30+
<plugins>
31+
<plugin>
32+
<groupId>org.apache.maven.plugins</groupId>
33+
<artifactId>maven-shade-plugin</artifactId>
34+
<version>3.2.4</version>
35+
<executions>
36+
<execution>
37+
<phase>package</phase>
38+
<goals>
39+
<goal>shade</goal>
40+
</goals>
41+
<configuration>
42+
<transformers>
43+
<transformer
44+
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
45+
<mainClass>main.java.pprrankserver.PprRankServer</mainClass>
46+
</transformer>
47+
</transformers>
48+
</configuration>
49+
</execution>
50+
</executions>
51+
</plugin>
52+
</plugins>
53+
</build>
54+
</project>

readme.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Headphone Predicted Preference Rating List
2+
Pierre Aubert's [Spinorama loudspeaker measurement collection list](https://pierreaubert.github.io/spinorama) is an outstanding community resource which presents a large collection of [CEA2034](https://webstore.ansi.org/Standards/CEA/cea20342015ansi) loudspeaker system measurements (spinoramas) and uses them to provide predicted preference ratings using Dr.Sean Olive's [Multiple Regression Model for Predicting Loudspeaker Preference Using Objective Measurements](https://www.aes.org/e-lib/browse.cfm?elib=12794).
3+
4+
Dr.Olive's predicted preference rating model for loudspeakers is a powerful implement with a near-perfect correlation to listener preference (r = 0.995). While this is an outstanding achievement and can be relied upon to provide tuning guidelines for speakers the same system is not predictive of listener preference for other audio devices like in-ear-monitors or headphones.
5+
6+
Headphones create a more complicated acoustic system than speakers to do due to the complex and differing acoustic properties of the human head, pinnae, and ear canal. As a consequence of that reality, systems for predicting the preference that listeners will show for different headphones have been slower to develop.
7+
8+
In 2018 Dr.Olive, MSc Todd Welti and BSc Omid Khonsari published a paper on their new [Statistical Model that Predicts Listener's Preference Ratings of Around-Ear and Over-Ear Headphones](https://www.aes.org/e-lib/browse.cfm?elib=19436). In that same paper they demonstrated that their model could predict listener preference near perfectly.
9+
10+
Despite the existence of a headphone predicted preference rating and application yet exists that catalogues headphone measurements from different sources and calculates their PPR in the same manner as Pierre Aubert's Spinorama list.
11+
12+
Developing a mono-dimensional list of headphones and their predictive preference scores is important as [there is no correlation between headphone price and performance](https://asa.scitation.org/doi/full/10.1121/1.4984044) and a near absence of resources which rigorously and quantitavely evaluate the performance of headphones.
13+
14+
This resource aims help fill that gap in the same manner as Mr.Aubert's Spinorama list. Please contact me at tadgh@tadghwagstaff.com for more information or if you want me to add a dataset to the list.
15+
16+
17+
## Stuff I have yet to write:
18+
1) Limitations of Existing Headphone Measurements
19+
20+
2) Limitations of Headphone PPR
21+
22+
3) Assessing the Quality of Headphone Measurements
23+
24+
4) Complications of different HRTFs in Preference
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package main.java.DatasetUploader;
2+
3+
import java.util.ArrayList;
4+
5+
public class Dataset{
6+
7+
private String name;
8+
private ArrayList<Double> magnitudes;
9+
private Double ppr;
10+
11+
public Dataset(String name, ArrayList<Double> magnitudes){
12+
this.name = name;
13+
this.magnitudes = magnitudes;
14+
}
15+
16+
public String getName() {
17+
return name;
18+
}
19+
public ArrayList<Double> getMagnitudes() {
20+
return magnitudes;
21+
}
22+
23+
public Double getPpr() {
24+
return ppr;
25+
}
26+
27+
public calculatePpr(){
28+
29+
//-----------------------CALCULATE PPR FROM RELATIVE MAGNITUDE | CONTEXT-----------------
30+
31+
//Here comes the nerdy bit, bare with me and read see the introductory readme at the root of this repo to read about the science behind
32+
//purpose of the prefered preference rating.
33+
34+
//First let's define our anatomical transfer function. ATFs (aka HRTFs) are essentially models of how the sound recieved at the eardrum of
35+
//the user of a supra/circum aural audio device will have been transformed by the space between the ear drum and device transducer.
36+
37+
//A headphone tuned to sound like a speaker at a distance would sound terrible connected to your head because of the changes in acoustic
38+
//environment created by attaching it to your head. Therefore, if we can figure out what changes a signal undergoes before reaching the
39+
//eardrum of an average person we can make a headphone that accounts for them in its design and sounds undistorted.
40+
41+
//Unbelievably clever people like Dr.Sean Olive and Dr.Floyd Toole have refined these transfer functions over time through a mixture of
42+
//theoretical and experimental data. Harman's AE/OE target is the culmination of their work.
43+
44+
//Olive's Predicted Preference Rating calculation looks at measurements of a headphone's output and compares data derived from that measurement
45+
//to the Harman ATF to derive a prediction of how natural a headphone will sound to an average listener. In order to perform this calculation
46+
//we first need two pieces of information.
47+
48+
//The first is our list of preferred frequencies. When we analyse an acoustic signal we're usually talking about how a given sound source produces
49+
//some frequencies of noises with more emphasis, or more quietly, than others. Imagine a performance with an oboe and a picollo. In person both
50+
//instruments sound harmoniously balanced. Even if a perfect recording of this performance was made, if it was played back through a poorly designed
51+
//speaker or headphone, the oboe may eclipse the picollo due to the speaker or headphone producing much louder lower frequency (deeper) noises than
52+
//higher frequency (tinnier) noises. Another poorly designed device might do the opposite.
53+
54+
//Audio measurements operate by producing all frequencies of sound at equal volumes at different times, measuring how loud the output is, and comparing
55+
//the magnitude of output at each frequency to the others. Using that data we can see clearly where that variety of distortion is.
56+
57+
58+
//--------------------------------------IMPORTANT DATA-------------------------------------
59+
60+
//We only need to measure some of the frequencies to ensure that we have a good sample of data, but for our measurements and analysis to work correctly
61+
//we need to have an agreed upon set of frequencies to consider. So first we need to declare a list of preferred frequencies.
62+
63+
int[] preferredFrequencies = {20,21,22,24,25,27,28,30,32,34,36,38,40,43,45,48,50,53,56,60,63,67,71,75,80,85,90,95,100,106,112,118,125,132,140,150,
64+
160,170,180,190,200,212,224,236,250,265,280,300,315,335,355,375,400,425,450,475,500,530,560,600,630,670,710,750,800,850,900,950,1000,1060,1120,1180,
65+
1250,1320,1400,1500,1600,1700,1800,1900,2000,2120,2240,2360,2500,2650,2800,3000,3150,3350,3550,3750,4000,4250,4500,4750,5000,5300,5600,6000,6300,
66+
6700,7100,7500,8000,8500,9000,9500,10000,10600,11200,11800,12500,13200,14000,15000,16000,17000,18000,19000,20000};
67+
68+
//Now we can declare what the ideal magnitude for each frequency should be
69+
70+
float[] harmanAeOe2018 = {4.7,4.84,4.88,4.87,4.86,4.85,4.83,4.8,4.76,4.72,4.66,4.61,4.54,4.47,4.38,4.28,4.16,4.03,3.89,3.74,3.58,3.41,3.23,3.04,
71+
2.83,2.61,2.38,2.14,1.89,1.61,1.33,1.04,0.75,0.47,0.2,-0.06,-0.32,-0.55,-0.77,-0.96,-1.11,-1.21,-1.25,-1.24,-1.18,-1.08,-0.96,-0.83,-0.7,
72+
-0.57,-0.46,-0.37,-0.29,-0.23,-0.16,-0.08,0,0.1,0.2,0.3,0.39,0.48,0.55,0.61,0.66,0.69,0.73,0.77,0.84,0.96,1.13,1.36,1.66,2.01,2.44,2.94,3.51,
73+
4.13,4.79,5.45,6.11,6.73,7.3,7.81,8.25,8.62,8.92,9.16,9.33,9.42,9.41,9.28,9.03,8.68,8.24,7.77,7.29,6.83,6.4,5.97,5.53,5.05,4.52,3.92,3.27,2.58,
74+
1.84,1.04,0.14,-0.85,-1.93,-3.07,-4.18,-5.22,-6.16,-7.11,-8.3,-10.07,-12.8,-16.83,-22.32};
75+
76+
//For one of our equation's variables we need to provide the frequencies as co-ordinates on an x-axis. Because human hearing percieves frequencies
77+
//logarithmically our preferred frequencies proceeed exponentially. We can lay out that exponential progress as the linear co-ordinates the regression
78+
//method accepts and it'll work just fine. I've done this in advance so we don't need to redo that calculation everytime this method is called.
79+
80+
double[] preferredFrequenciesLin = {2.995732274,3.054001182,3.109060959,3.161246712,3.218875825,3.277144733,3.33220451,3.401197382,3.449987546,3.511545439,
81+
3.569532696,3.624340933,3.688879454,3.749504076,3.80666249,3.860729711,3.912023005,3.970291914,4.025351691,4.094344562,4.143134726,4.204692619,4.262679877,
82+
4.317488114,4.382026635,4.442651256,4.49980967,4.553876892,4.605170186,4.663439094,4.718498871,4.770684624,4.828313737,4.882801923,4.941642423,5.010635294,
83+
5.075173815,5.135798437,5.192956851,5.247024072,5.298317367,5.356586275,5.411646052,5.463831805,5.521460918,5.579729826,5.634789603,5.703782475,5.752572639,
84+
5.814130532,5.872117789,5.926926026,5.991464547,6.052089169,6.109247583,6.163314804,6.214608098,6.272877007,6.327936784,6.396929655,6.445719819,6.507277712,
85+
6.56526497,6.620073207,6.684611728,6.745236349,6.802394763,6.856461985,6.907755279,6.966024187,7.021083964,7.073269717,7.13089883,7.185387016,7.244227516,
86+
7.313220387,7.377758908,7.43838353,7.495541944,7.549609165,7.60090246,7.659171368,7.714231145,7.766416898,7.824046011,7.882314919,7.937374696,8.006367568,
87+
8.055157732,8.116715625,8.174702882,8.229511119,8.29404964,8.354674262,8.411832676,8.465899897,8.517193191,8.5754621,8.630521877,8.699514748,8.748304912,
88+
8.809862805,8.867850063,8.9226583,8.987196821,9.047821442,9.104979856,9.159047078,9.210340372,9.26860928,9.323669057,9.37585481,9.433483923,9.487972109,
89+
9.546812609,9.61580548,9.680344001,9.740968623,9.798127037,9.852194258,9.903487553}
90+
91+
//---------------------FINDING THE VARIABLES | STANDARD DEVIATION OF SAMPLE----------------
92+
93+
//Calculating PPR isn't all that complicated in and of itself, but we do need to calculate two important variables before we can begin. The first of these
94+
//is essentially a refined measure of how much the samples in the dataset deviate from Harman's AE OE target. To get standard deviation you need to find
95+
//the mean deviation, find each sample's deviation from that mean, square them, sum the squares, divide them by the sample size, and then find the square
96+
//root of the remaining figure.
97+
98+
//Step 1: Find mean deviation
99+
100+
ArrayList<Float> deviations;
101+
Float totalDeviation;
102+
103+
for(int i = 16; i < 108; i++){
104+
Double magnitude = this.magnitudes.get(i);
105+
Float target = harmanAeOe2018[i];
106+
Float deviation = magnitude - harmanAeOe2018[i];
107+
deviations.add(deviation);
108+
totalDeviation += deviation;
109+
}
110+
111+
Float meanDeviation = totalDeviation / deviations.size();
112+
113+
//Step 2: Find each magnitude's deviation from the mean deviation
114+
ArrayList<Float> deviationsFromMean;
115+
116+
for(int i = 16; i < 108; i++){
117+
Double magnitude = this.magnitudes.get(i);
118+
Double deviationFromMean = magnitude - meanDeviation;
119+
deviationsFromMean.add(deviationFromMean);
120+
}
121+
122+
//Step 3: Square each deviation from the mean and sum them
123+
Float sumOfDeviationsFromMeanSquared;
124+
125+
for(Float deviationFromMean : deviationsFromMean){
126+
Float squaredDeviationMinusMean = deviationFromMean * deviationFromMean;
127+
sumOfDeviationsFromMeanSquared += squaredDeviationMinusMean;
128+
}
129+
130+
//Step 4: Find variance
131+
Float variance = sumOfDeviationMinusMeanSquared / 93;
132+
133+
//Step 5: Find square root of the variance
134+
Float standardDeviation = Math.sqrt(Double.parseDouble(variance));
135+
136+
137+
//---------------------FINDING THE VARIABLES | SLOPE OF LINEAR REGRESSION OF MAGNITUDES----------------
138+
139+
//Ok, now that we have our standard deviation calculated we need to find the slope of the linear equation that describes
140+
//the curve of our dataset's deviancy from our ATF. I use Apache Commons Math 4's statistics package here because it's
141+
//very readable
142+
143+
SimpleRegression regression = new SimpleRegression();
144+
145+
//We have to load our data into the regression before slope can be estimated
146+
for(int i = 16; i < (samplesPer - 12); i++){
147+
regression.addData(dataset.get(preferredFrequenciesLin[i], dataset.get(i)));
148+
}
149+
150+
//Now we can calculate our slope
151+
Double slope = regression.getSlope();
152+
153+
//Technically we need the absolute value of the slope so
154+
Double absSlope = Math.abs(slope);
155+
156+
//Now that we have our two variables, calculating PPR is simple
157+
Double ppr = 114.490443008238 - (12.6217151040598 * standardDeviation) - (15.5163857197367 * absSlope);
158+
159+
//Now we know our dataset's ppr!
160+
this.ppr = ppr;
161+
}
162+
163+
164+
}

0 commit comments

Comments
 (0)