aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2017-08-08 20:25:31 -0700
committerChris Robinson <[email protected]>2017-08-08 20:30:26 -0700
commitad3c4b81e2e6547fba7e66615a7ffd2fb69eb3bc (patch)
tree6101fc01ed52f459ce3d5359abe57d0113d7c047
parent530002e168c907bd3468cd0fefaec2565e248bbd (diff)
Add experimental support for 24-bit, dual-ear HRTFs
Currently makehrtf only handles 24-bit output, not dual-ear, and only when given the --experimental switch. Files produced this way will not be guaranteed future compatibility. When the mhr format is also updated with multi-distance measurements, the experimental switch can go away.
-rw-r--r--Alc/hrtf.c233
-rw-r--r--utils/makehrtf.c50
2 files changed, 272 insertions, 11 deletions
diff --git a/Alc/hrtf.c b/Alc/hrtf.c
index 56a4c670..14e350c0 100644
--- a/Alc/hrtf.c
+++ b/Alc/hrtf.c
@@ -54,6 +54,8 @@ struct HrtfEntry {
static const ALchar magicMarker00[8] = "MinPHR00";
static const ALchar magicMarker01[8] = "MinPHR01";
+/* FIXME: Set with the right number when finalized. */
+static const ALchar magicMarker02[18] = "MinPHRTEMPDONOTUSE";
/* First value for pass-through coefficients (remaining are 0), used for omni-
* directional sounds. */
@@ -383,9 +385,16 @@ static ALushort GetLE_ALushort(const ALubyte **data, size_t *len)
return ret;
}
-static ALint GetLE_ALuint(const ALubyte **data, size_t *len)
+static ALint GetLE_ALint24(const ALubyte **data, size_t *len)
{
- ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
+ ALint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16);
+ *data += 3; *len -= 3;
+ return (ret^0x800000) - 0x800000;
+}
+
+static ALuint GetLE_ALuint(const ALubyte **data, size_t *len)
+{
+ ALuint ret = (*data)[0] | ((*data)[1]<<8) | ((*data)[2]<<16) | ((*data)[3]<<24);
*data += 4; *len -= 4;
return ret;
}
@@ -711,6 +720,217 @@ static struct Hrtf *LoadHrtf01(const ALubyte *data, size_t datalen, const char *
return Hrtf;
}
+#define SAMPLETYPE_S16 0
+#define SAMPLETYPE_S24 1
+
+#define CHANTYPE_LEFTONLY 0
+#define CHANTYPE_LEFTRIGHT 1
+
+static struct Hrtf *LoadHrtf02(const ALubyte *data, size_t datalen, const char *filename)
+{
+ const ALubyte maxDelay = HRTF_HISTORY_LENGTH-1;
+ struct Hrtf *Hrtf = NULL;
+ ALboolean failed = AL_FALSE;
+ ALuint rate = 0;
+ ALubyte sampleType;
+ ALubyte channelType;
+ ALushort irCount = 0;
+ ALushort irSize = 0;
+ ALubyte evCount = 0;
+ const ALubyte *azCount = NULL;
+ ALushort *evOffset = NULL;
+ ALfloat (*coeffs)[2] = NULL;
+ ALubyte (*delays)[2] = NULL;
+ ALsizei i, j;
+
+ if(datalen < 6)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, 6, datalen);
+ return NULL;
+ }
+
+ rate = GetLE_ALuint(&data, &datalen);
+ sampleType = GetLE_ALubyte(&data, &datalen);
+ channelType = GetLE_ALubyte(&data, &datalen);
+
+ irSize = GetLE_ALubyte(&data, &datalen);
+
+ evCount = GetLE_ALubyte(&data, &datalen);
+
+ if(sampleType > SAMPLETYPE_S24)
+ {
+ ERR("Unsupported sample type: %d\n", sampleType);
+ failed = AL_TRUE;
+ }
+ if(channelType > CHANTYPE_LEFTRIGHT)
+ {
+ ERR("Unsupported channel type: %d\n", channelType);
+ failed = AL_TRUE;
+ }
+
+ if(irSize < MIN_IR_SIZE || irSize > MAX_IR_SIZE || (irSize%MOD_IR_SIZE))
+ {
+ ERR("Unsupported HRIR size: irSize=%d (%d to %d by %d)\n",
+ irSize, MIN_IR_SIZE, MAX_IR_SIZE, MOD_IR_SIZE);
+ failed = AL_TRUE;
+ }
+ if(evCount < MIN_EV_COUNT || evCount > MAX_EV_COUNT)
+ {
+ ERR("Unsupported elevation count: evCount=%d (%d to %d)\n",
+ evCount, MIN_EV_COUNT, MAX_EV_COUNT);
+ failed = AL_TRUE;
+ }
+ if(failed)
+ return NULL;
+
+ if(datalen < evCount)
+ {
+ ERR("Unexpected end of %s data (req %d, rem "SZFMT"\n", filename, evCount, datalen);
+ return NULL;
+ }
+
+ azCount = Get_ALubytePtr(&data, &datalen, evCount);
+
+ evOffset = malloc(sizeof(evOffset[0])*evCount);
+ if(azCount == NULL || evOffset == NULL)
+ {
+ ERR("Out of memory.\n");
+ failed = AL_TRUE;
+ }
+
+ if(!failed)
+ {
+ for(i = 0;i < evCount;i++)
+ {
+ if(azCount[i] < MIN_AZ_COUNT || azCount[i] > MAX_AZ_COUNT)
+ {
+ ERR("Unsupported azimuth count: azCount[%d]=%d (%d to %d)\n",
+ i, azCount[i], MIN_AZ_COUNT, MAX_AZ_COUNT);
+ failed = AL_TRUE;
+ }
+ }
+ }
+
+ if(!failed)
+ {
+ evOffset[0] = 0;
+ irCount = azCount[0];
+ for(i = 1;i < evCount;i++)
+ {
+ evOffset[i] = evOffset[i-1] + azCount[i-1];
+ irCount += azCount[i];
+ }
+
+ coeffs = malloc(sizeof(coeffs[0])*irSize*irCount);
+ delays = malloc(sizeof(delays[0])*irCount);
+ if(coeffs == NULL || delays == NULL)
+ {
+ ERR("Out of memory.\n");
+ failed = AL_TRUE;
+ }
+ }
+
+ if(!failed)
+ {
+ size_t reqsize = 2*irSize*irCount + irCount;
+ if(datalen < reqsize)
+ {
+ ERR("Unexpected end of %s data (req "SZFMT", rem "SZFMT"\n",
+ filename, reqsize, datalen);
+ failed = AL_TRUE;
+ }
+ }
+
+ if(!failed)
+ {
+ if(channelType == CHANTYPE_LEFTONLY || channelType == CHANTYPE_LEFTRIGHT)
+ {
+ if(sampleType == SAMPLETYPE_S16)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ coeffs[i*irSize + j][0] = GetLE_ALshort(&data, &datalen) / 32768.0f;
+ }
+ else if(sampleType == SAMPLETYPE_S24)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ coeffs[i*irSize + j][0] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
+ }
+ }
+ if(channelType == CHANTYPE_LEFTRIGHT)
+ {
+ if(sampleType == SAMPLETYPE_S16)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ coeffs[i*irSize + j][1] = GetLE_ALshort(&data, &datalen) / 32768.0f;
+ }
+ else if(sampleType == SAMPLETYPE_S24)
+ for(i = 0;i < irCount;i++)
+ {
+ for(j = 0;j < irSize;j++)
+ coeffs[i*irSize + j][1] = GetLE_ALint24(&data, &datalen) / 8388608.0f;
+ }
+ }
+ if(channelType == CHANTYPE_LEFTONLY || channelType == CHANTYPE_LEFTRIGHT)
+ {
+ for(i = 0;i < irCount;i++)
+ {
+ delays[i][0] = GetLE_ALubyte(&data, &datalen);
+ if(delays[i][0] > maxDelay)
+ {
+ ERR("Invalid delays[%d][0]: %d (%d)\n", i, delays[i][0], maxDelay);
+ failed = AL_TRUE;
+ }
+ }
+ }
+ if(channelType == CHANTYPE_LEFTRIGHT)
+ {
+ for(i = 0;i < irCount;i++)
+ {
+ delays[i][1] = GetLE_ALubyte(&data, &datalen);
+ if(delays[i][1] > maxDelay)
+ {
+ ERR("Invalid delays[%d][1]: %d (%d)\n", i, delays[i][1], maxDelay);
+ failed = AL_TRUE;
+ }
+ }
+ }
+ }
+
+ if(!failed)
+ {
+ if(channelType == CHANTYPE_LEFTONLY)
+ {
+ /* Mirror the left ear responses to the right ear. */
+ for(i = 0;i < evCount;i++)
+ {
+ ALushort evoffset = evOffset[i];
+ ALubyte azcount = azCount[i];
+ for(j = 0;j < azcount;j++)
+ {
+ ALsizei lidx = evoffset + j;
+ ALsizei ridx = evoffset + ((azcount-j) % azcount);
+ ALsizei k;
+
+ for(k = 0;k < irSize;k++)
+ coeffs[ridx*irSize + k][1] = coeffs[lidx*irSize + k][0];
+ delays[ridx][1] = delays[lidx][0];
+ }
+ }
+ }
+
+ Hrtf = CreateHrtfStore(rate, irSize, evCount, irCount, azCount,
+ evOffset, coeffs, delays, filename);
+ }
+
+ free(evOffset);
+ free(coeffs);
+ free(delays);
+ return Hrtf;
+}
+
static void AddFileEntry(vector_EnumeratedHrtf *list, const_al_string filename)
{
@@ -1064,8 +1284,15 @@ struct Hrtf *GetLoadedHrtf(struct HrtfEntry *entry)
rsize = fmap.len;
}
- if(rsize < sizeof(magicMarker01))
+ if(rsize < sizeof(magicMarker02))
ERR("%s data is too short ("SZFMT" bytes)\n", name, rsize);
+ else if(memcmp(rdata, magicMarker02, sizeof(magicMarker02)) == 0)
+ {
+ TRACE("Detected data set format v2\n");
+ hrtf = LoadHrtf02(rdata+sizeof(magicMarker02),
+ rsize-sizeof(magicMarker02), name
+ );
+ }
else if(memcmp(rdata, magicMarker01, sizeof(magicMarker01)) == 0)
{
TRACE("Detected data set format v1\n");
diff --git a/utils/makehrtf.c b/utils/makehrtf.c
index 259b717f..963528bb 100644
--- a/utils/makehrtf.c
+++ b/utils/makehrtf.c
@@ -196,6 +196,19 @@
// response protocol 01.
#define MHR_FORMAT ("MinPHR01")
+#define MHR_FORMAT_EXPERIMENTAL ("MinPHRTEMPDONOTUSE")
+
+// Sample and channel type enum values
+typedef enum SampleTypeT {
+ ST_S16 = 0,
+ ST_S24 = 1
+} SampleTypeT;
+
+typedef enum ChannelTypeT {
+ CT_LEFTONLY = 0,
+ CT_LEFTRIGHT = 1
+} ChannelTypeT;
+
// Byte order for the serialization routines.
typedef enum ByteOrderT {
BO_NONE,
@@ -268,6 +281,8 @@ typedef struct SourceRefT {
// the resulting HRTF.
typedef struct HrirDataT {
uint mIrRate;
+ SampleTypeT mSampleType;
+ ChannelTypeT mChannelType;
uint mIrCount;
uint mIrSize;
uint mIrPoints;
@@ -1869,7 +1884,7 @@ static int WriteBin4(const ByteOrderT order, const uint bytes, const uint32 in,
}
// Store the OpenAL Soft HRTF data set.
-static int StoreMhr(const HrirDataT *hData, const char *filename)
+static int StoreMhr(const HrirDataT *hData, const int experimental, const char *filename)
{
uint e, step, end, n, j, i;
uint dither_seed;
@@ -1881,10 +1896,17 @@ static int StoreMhr(const HrirDataT *hData, const char *filename)
fprintf(stderr, "Error: Could not open MHR file '%s'.\n", filename);
return 0;
}
- if(!WriteAscii(MHR_FORMAT, fp, filename))
+ if(!WriteAscii(experimental ? MHR_FORMAT_EXPERIMENTAL : MHR_FORMAT, fp, filename))
return 0;
if(!WriteBin4(BO_LITTLE, 4, (uint32)hData->mIrRate, fp, filename))
return 0;
+ if(experimental)
+ {
+ if(!WriteBin4(BO_LITTLE, 1, (uint32)hData->mSampleType, fp, filename))
+ return 0;
+ if(!WriteBin4(BO_LITTLE, 1, (uint32)hData->mChannelType, fp, filename))
+ return 0;
+ }
if(!WriteBin4(BO_LITTLE, 1, (uint32)hData->mIrPoints, fp, filename))
return 0;
if(!WriteBin4(BO_LITTLE, 1, (uint32)hData->mEvCount, fp, filename))
@@ -1900,13 +1922,17 @@ static int StoreMhr(const HrirDataT *hData, const char *filename)
dither_seed = 22222;
for(j = 0;j < end;j += step)
{
+ const double scale = (!experimental || hData->mSampleType == ST_S16) ? 32767.0 :
+ ((hData->mSampleType == ST_S24) ? 8388607.0 : 0.0);
+ const int bps = (!experimental || hData->mSampleType == ST_S16) ? 2 :
+ ((hData->mSampleType == ST_S24) ? 3 : 0);
double out[MAX_TRUNCSIZE];
for(i = 0;i < n;i++)
- out[i] = TpdfDither(32767.0 * hData->mHrirs[j+i], &dither_seed);
+ out[i] = TpdfDither(scale * hData->mHrirs[j+i], &dither_seed);
for(i = 0;i < n;i++)
{
- v = (int)Clamp(out[i], -32768.0, 32767.0);
- if(!WriteBin4(BO_LITTLE, 2, (uint32)v, fp, filename))
+ v = (int)Clamp(out[i], -scale-1.0, scale);
+ if(!WriteBin4(BO_LITTLE, bps, (uint32)v, fp, filename))
return 0;
}
}
@@ -2697,7 +2723,7 @@ error:
* resulting data set as desired. If the input name is NULL it will read
* from standard input.
*/
-static int ProcessDefinition(const char *inName, const uint outRate, const uint fftSize, const int equalize, const int surface, const double limit, const uint truncSize, const HeadModelT model, const double radius, const OutputFormatT outFormat, const char *outName)
+static int ProcessDefinition(const char *inName, const uint outRate, const uint fftSize, const int equalize, const int surface, const double limit, const uint truncSize, const HeadModelT model, const double radius, const OutputFormatT outFormat, const int experimental, const char *outName)
{
char rateStr[8+1], expName[MAX_PATH_LEN];
TokenReaderT tr;
@@ -2706,6 +2732,8 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint
FILE *fp;
hData.mIrRate = 0;
+ hData.mSampleType = ST_S24;
+ hData.mChannelType = CT_LEFTONLY;
hData.mIrPoints = 0;
hData.mFftSize = 0;
hData.mIrSize = 0;
@@ -2779,7 +2807,7 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint
{
case OF_MHR:
fprintf(stdout, "Creating MHR data set file...\n");
- if(!StoreMhr(&hData, expName))
+ if(!StoreMhr(&hData, experimental, expName))
{
DestroyArray(hData.mHrtds);
DestroyArray(hData.mHrirs);
@@ -2826,6 +2854,7 @@ int main(const int argc, const char *argv[])
OutputFormatT outFormat;
uint outRate, fftSize;
int equalize, surface;
+ int experimental;
char *end = NULL;
HeadModelT model;
uint truncSize;
@@ -2860,6 +2889,7 @@ int main(const int argc, const char *argv[])
truncSize = DEFAULT_TRUNCSIZE;
model = DEFAULT_HEAD_MODEL;
radius = DEFAULT_CUSTOM_RADIUS;
+ experimental = 0;
argi = 2;
while(argi < argc)
@@ -2954,6 +2984,8 @@ int main(const int argc, const char *argv[])
inName = &argv[argi][3];
else if(strncmp(argv[argi], "-o=", 3) == 0)
outName = &argv[argi][3];
+ else if(strcmp(argv[argi], "--experimental") == 0)
+ experimental = 1;
else
{
fprintf(stderr, "Error: Invalid option '%s'.\n", argv[argi]);
@@ -2961,7 +2993,9 @@ int main(const int argc, const char *argv[])
}
argi++;
}
- if(!ProcessDefinition(inName, outRate, fftSize, equalize, surface, limit, truncSize, model, radius, outFormat, outName))
+ if(!ProcessDefinition(inName, outRate, fftSize, equalize, surface, limit,
+ truncSize, model, radius, outFormat, experimental,
+ outName))
return -1;
fprintf(stdout, "Operation completed.\n");
return 0;