How do I read the samplerate of an aiff?
-
- KVRist
- Topic Starter
- 65 posts since 24 Jun, 2005 from Berlin, Germany
Apparently the samplerate in AIFF is written as 80-bit floating point /extended / long double.
How do I read it in C++ Mac/Win?
How do I read it in C++ Mac/Win?
before time there was no time
- KVRAF
- 2237 posts since 25 Sep, 2014 from Specific Northwest
Convert it to WAV format and then open it?
The 80-bit float doesn't sound right to me. AIFF uses the same chunking scheme as WAV files, but with a few differences. WAV uses a four-byte integer to hold the sample rate as part of the "fmt " chunk.
It looks like AIFF uses a "comm" chunk instead. I'm not sure how long of an integer an "extended" type is, though.
ckID is always 'COMM'.
The 80-bit float doesn't sound right to me. AIFF uses the same chunking scheme as WAV files, but with a few differences. WAV uses a four-byte integer to hold the sample rate as part of the "fmt " chunk.
It looks like AIFF uses a "comm" chunk instead. I'm not sure how long of an integer an "extended" type is, though.
Code: Select all
4.0 COMMON CHUNK
The Common Chunk describes fundamental parameters of the sampled sound.
#define CommonID 'COMM' /* ckID for Common Chunk */
typedef struct {
ID ckID; /* 'COMM' */
long kDataSize;
short numChannels; /* # audio channels */
unsigned long numSampleFrames; /* # sample frames = samples/channel */
short sampleSize; /* # bits/sample */
extended sampleRate; /* sample_frames/sec */
ID compressionType; /* compression type ID code */
pstring compressionName; /* human-readable compression type name */
} CommonChunk;
I started on Logic 5 with a PowerBook G4 550Mhz. I now have a MacBook Air M1 and it's ~165x faster! So, why is my music not proportionally better?
- KVRAF
- 7890 posts since 12 Feb, 2006 from Helsinki, Finland
It is not an integer.
According to the specification: "80 bit IEEE Standard 754 floating point number (Standard Apple Numeric Environment [SANE] data type Extended)."
The x87 FPU uses these internally, but how they are stored in the file (or how to get them from the file to the FPU so you can then convert to something easier to work with), I have no clue.
- KVRAF
- 2237 posts since 25 Sep, 2014 from Specific Northwest
Crap. You're right, it's right there defined in the AIFF standard. I guess it helps to read the fore matter. But it really makes zero sense to have a fractional sample rate, to me. Even a four-byte fixed point would have better if you really needed it. Weird.
Anyways, this programmer was kind enough to write a utility:
https://github.com/brianmhunt/float80
I still stand by converting them all to WAV. AIFFs never took off.
Anyways, this programmer was kind enough to write a utility:
https://github.com/brianmhunt/float80
I still stand by converting them all to WAV. AIFFs never took off.
I started on Logic 5 with a PowerBook G4 550Mhz. I now have a MacBook Air M1 and it's ~165x faster! So, why is my music not proportionally better?
- KVRist
- 243 posts since 24 Aug, 2014
The same way you read other floats. Just pick appropriate type: https://en.wikipedia.org/wiki/Extended_ ... ge_support
-
- KVRist
- Topic Starter
- 65 posts since 24 Jun, 2005 from Berlin, Germany
__float80 is an unknown type.
before time there was no time
- KVRist
- 243 posts since 24 Aug, 2014
Did you try
Code: Select all
long double
-
- KVRist
- Topic Starter
- 65 posts since 24 Jun, 2005 from Berlin, Germany
long double test;
fpos=fmt_pos+16; fsetpos(fileHandle, &fpos);
fread(&test, sizeof(long double), 1, fileHandle);
no right result
fpos=fmt_pos+16; fsetpos(fileHandle, &fpos);
fread(&test, sizeof(long double), 1, fileHandle);
no right result
before time there was no time
-
- KVRist
- Topic Starter
- 65 posts since 24 Jun, 2005 from Berlin, Germany
long double is not the right type.
C++, Xcode
long double test=44100;
is coded as 00000000 000044AC 0E40527A FF7F0000
in aiff it is coded as 400EAC44 00000000 00004348 414E0000
There are some endian and other problems.
C++, Xcode
long double test=44100;
is coded as 00000000 000044AC 0E40527A FF7F0000
in aiff it is coded as 400EAC44 00000000 00004348 414E0000
There are some endian and other problems.
before time there was no time
- KVRian
- 799 posts since 25 Apr, 2011
If you're expecting to convert to an integer samplerate, I reckon you can just use the first 48 bits of the 80-bit number and do something like (*warning, untested pseudocode)
It might be an idea to round up 'man' by 1 if bit 48 is set in the original number.
This should handle a pretty wide range of values.
Code: Select all
exp=first 16 bits // (=0x400e)
exp=exp-0x3fff // (=0xf)
man=bits 16..47 // (=0xac440000)
rate=man>>(0x1f-exp) // (= man>>0x10 =0xac44 =44100dec)
This should handle a pretty wide range of values.
- KVRist
- 243 posts since 24 Aug, 2014
Try this:
Code: Select all
#include <cstdio>
#include <cstdint>
double parse_float80be(uint8_t* data) {
double sign = ((data[0] >> 7) & 1) ? -1 : 1;
uint16_t exponent = (uint16_t(data[0] & 0x7F) << 8) | data[1];
double base = ((data[2] >> 7) & 1) ? 1 : 0;
uint64_t mantissa = data[2] & 0x7F;
for (int i = 1; i < 8; ++i) {
mantissa = (mantissa << 8)|data[2 + i];
}
//value = (-1) ^ s * (base + m / 2 ^ 63) * 2 ^ (e - 16383)
return sign * (base + double(mantissa)/(uint64_t(1) << 63)) * exp2(double(int32_t(exponent - 16383)));
}
double read_extended(FILE* f) {
uint8_t data[10];
fread(&data, sizeof(data), 1, f);
return parse_float80be(data);
}
-
- KVRist
- Topic Starter
- 65 posts since 24 Jun, 2005 from Berlin, Germany
Hi kryptonaut and Vokbuz,
I can confirm that I have tested both of your methods between 6kHz and 192 kHz and both seem to work.
This is how I implemented the kryptonaut-version, I like it because it is really short:
uint32_t exp= 256*TempByte[0]+TempByte[1]; //first 16 bits
exp=exp-0x3fff;
uint32_t man = 16777216 * TempByte[2] + 65536 * TempByte[3] + 256 * TempByte[4] + TempByte[5]; //bits 16..47
uint32_t rate= (uint32_t) (man >> (0x1f-exp));
Thank you guys!
I can confirm that I have tested both of your methods between 6kHz and 192 kHz and both seem to work.
This is how I implemented the kryptonaut-version, I like it because it is really short:
uint32_t exp= 256*TempByte[0]+TempByte[1]; //first 16 bits
exp=exp-0x3fff;
uint32_t man = 16777216 * TempByte[2] + 65536 * TempByte[3] + 256 * TempByte[4] + TempByte[5]; //bits 16..47
uint32_t rate= (uint32_t) (man >> (0x1f-exp));
Thank you guys!
before time there was no time
-
- KVRist
- Topic Starter
- 65 posts since 24 Jun, 2005 from Berlin, Germany
The AIFF-Samplerate-Format is made for a great variety of numbers, including MHz, and non integer values. We do not need them for music. The main task is to distingwish between 44100, 48000, 88200, ... 192000 Hz. 18 bit integer is enough for us.
before time there was no time
- KVRian
- 799 posts since 25 Apr, 2011
Glad you found the integer code useful - if you want to round rather than truncate fractions you could replace the last line of your code with something like
which ought to round fractional numbers a bit better.
Note that it assumes that exp<0x1f. Also the code makes no effort to handle negative numbers - it's a bit of a fudge that works for integers in the expected range.
Code: Select all
uint32_t rate= (uint32_t) (man >> (0x1e-exp));
t_rate+=1;
t_rate>>=1;
Note that it assumes that exp<0x1f. Also the code makes no effort to handle negative numbers - it's a bit of a fudge that works for integers in the expected range.