How do I read the samplerate of an aiff?

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Apparently the samplerate in AIFF is written as 80-bit floating point /extended / long double.

How do I read it in C++ Mac/Win?
before time there was no time

Post

Convert it to WAV format and then open it? :lol:

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;
ckID is always 'COMM'.
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? :(

Post

syntonica wrote: Fri Apr 23, 2021 1:24 amI'm not sure how long of an integer an "extended" type is, though.
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.

Post

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. :lol:
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? :(

Post

timefreezer wrote: Thu Apr 22, 2021 11:02 pm How do I read it in C++ Mac/Win?
The same way you read other floats. Just pick appropriate type: https://en.wikipedia.org/wiki/Extended_ ... ge_support

Post

__float80 is an unknown type.
before time there was no time

Post

timefreezer wrote: Fri Apr 23, 2021 10:51 am __float80 is an unknown type.
Did you try

Code: Select all

long double
?

Post

long double test;
fpos=fmt_pos+16; fsetpos(fileHandle, &fpos);
fread(&test, sizeof(long double), 1, fileHandle);

no right result
before time there was no time

Post

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.
before time there was no time

Post

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)

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)
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.

Post

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);
}

Post

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!
before time there was no time

Post

I don't think it is a good idea to round value that potentially may have fraction.

Post

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

Post

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

Code: Select all

uint32_t rate= (uint32_t) (man >> (0x1e-exp));
t_rate+=1;
t_rate>>=1;
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. :)

Post Reply

Return to “DSP and Plugin Development”