Se connecter
Se connecter

ou
Créer un compte

ou

Moteurs audio, 32 bits / 64 bits, distinguer le vrai du faux .

  • 185 réponses
  • 23 participants
  • 27 240 vues
  • 31 followers
Sujet de la discussion Moteurs audio, 32 bits / 64 bits, distinguer le vrai du faux .
Bonjour à tous les audacieux qui auront le courage de se lancer dans cette réflexion ...

voilà, je me pose la question suivante :

a-t-il déjà été fait un comparatif sérieux des moteurs audio qui animent les DAWs les plus réputées (ou les plus répendues ...) ?

Depuis quelques temps, on voit apparaître comme principal argument de renouvellement de certains logiciels, le fait que leur moteur audio passe au 64 bits. Y'a-t-il un gain réel à se lancer dans le 64 bits en tant que moteur de mixage (et pas en tant que moteur de système d'exploitation ... :clin: ) ?

Personnellement, je pense bien qu'il doit y avoir un avantage net puisque nous massacrons joyeusement de plus en plus le signal à coup de plug-ins déjantés, et qu'une précision accrue dans les calculs permet de conserver une cohérence dans le signal traité (ne serait-ce qu'au niveau de sa phase ...). Sans compter que des logiciels aux ambitions très modestes comme Tracktion 2 se sont armés de ces moteurs 64 bits, alors que des plates-formes considérées comme "haut de gamme", comme ProTools, pour ne pas le citer, en reste pour l'instant au 32 bits (alors que ses prétentions qualitatives et pécuniaires sont pour le moins beaucoup plus élevées). Et en outre, des logiciels comme Samplitude ou Pyramix ont des moteurs en 32 bits mais extrêmement réputés (car très bien programmés semble-t-il) pour leur respect du signal. Une vache n'y retrouverait pas son veau ...

Un rapide état des lieux :

moteurs 64 bits :
Sonar 6
Cubase 4
Sequoia 9
Tracktion 2

moteurs 32 bits : (je vais en oublier c'est sûr)
Samplitude 9
Pyramix 5
Nuendo 3
Pro Tools ??? (je me perds dans les versions)
Kristal Audio Engine (faudrait pas oublier les gratuits ...)

voilà, merci à ceux qui voudront bien partager cette réflexion avec moi ...

:bravo:
Si vis pacem para bellum
Afficher le sujet de la discussion
141

Citation : Mais il faut bien se rendre compte d'une chose: si on mixe une piste a 0 dB et une a -80 dB (ce qui est stupide en general, mais passons), on utilise encore tous les bits de precision (les 23),

Mais non mon Pov Gabou, c'est vrai pour le résultat de la sommation, mais ça ne l'est pas en ce qui concerne la résolution de chacune des pistes.

Nous sommes bien d'accord que dans le monde du 32 bits flottant, la résolution réelle est de 24 bits. La piste à -80dB conserve jusqu'à la sommation sa résolution de 24bits, mais une fois ajoutée à une autre piste dont les crètes sont amenées à 0dBfs, nous somme bien en valeur réelle à -80dB, soit une résolution de 10bits sur les 24 de départ.

Mais je t'accorde que je me suis placé au départ dans un cas d'école, qui ira détecter la perte de qualité de ce son alors que dans le mix il se situe à -80dB ? Mais c'est quand même la situation rencontrée sur les fades out.

Sur la dénormalisation, c'est vrai que c'est un cas un peu particulier, c'est la raison de "c'est un peu ce qui se passait", sinon, j'aurais écrit "c'est ce qui se passait" ;)

JM
142

Citation :
Mais non mon Pov Gabou, c'est vrai pour le résultat de la sommation, mais ça ne l'est pas en ce qui concerne la résolution de chacune des pistes.

nous somme bien en valeur réelle à -80dB, soit une résolution de 10bits sur les 24 de départ.



Je comprends pas du tout d'ou tu sors ces 10 bits de precision ?

Je suis pas d'accord sur le fait que tu perdes beaucoup en precision en ayant deux pistes mixees l'une a -80 dB et l'autre a 0 dB. Si en absolu on a une piste qui va entre -1 et 1 (0 dB), et une autre qui va entre -1e-4 et 1e-4 (-80 dB), on va avoir en sommant entre 1 et -1. Donc dans le resultat final, on a au pire seulement 1e-7 d'amplitude de resolution, ce qui fait encore 60 dB sur le signal a -80 dB, et bien les 140 et quelques sur le resultat final... Et la sommation est faite a la fin de toute facon, avant que ce soit passe a la carte sont (qui elle n'a que 24 bits de precision au mieux), sur une representation fixe.

Ah, je vois d'ou tu peux sortir ces 10 bits de precision: 1 bit de precision donne 6 dB de dynamique ? Attention, cette regle est totalement fausse en representation flottante, qui n'est pas lineaire du tout.
143
Me suis amuse a faire un petit programme en python qui calcule la difference max et en moyenne entre le summing en 32 bits et 64 bits:

Citation :

#! /usr/bin/env python                                                                   
# Last Change: Mon Oct 30 01:00 PM 2006 J

import numpy as N

n = int(1e5)
dbd = -80

# A is double, between -1 and 1 (0dB)
A = 2*N.random.rand((n))-1
# B is double, between -1e-4 and 1e4
B = (2*N.random.rand((n))-1) * 10 ** (dbd/20)

S = A + B

powerA = N.sqrt(N.sum(A**2)) * 1./n
powerB = N.sqrt(N.sum(B**2)) * 1./n
print "Real db Diff (power) is %f" % (20*N.log10(powerA/powerB))

A32 = N.float32(A)
B32 = N.float32(B)

S32 = A32 + B32

# numpy broadcast to 64 bits automatically, so the difference is done
# in 64 bits
diff = S32 - S

powerdiff = N.sqrt(N.sum(diff ** 2)) * 1./n
maxdiff = N.max(N.abs(diff))

print "average diff of dynamic compared to bigger signal (0 dB) is %f" % (20*N.log10(powerdiff))
print "worse dynamic compared to bigger signal (0 dB) is %f" % (20*N.log10(maxdiff))



Et ca me donne autour de 220 dB de dynamique pour la difference entre 32 bits et 64 bits summing, et la pire valeur sur 100000 echantillons est encore a 144 dB.

Et comme je suis motive, j'ai fait la meme chose en C, pour essayer de plus grosses valeurs et pour que tout le monde puisse essayer (attention, c'est programme a la hache, donc ca prend pas mal de memoire. Sur mon pc du labo, j'ai 2 Go de ram avec 3 de swap, le programme doit consommer autour de 10^7 * 8 * 8 octets, ce qui fai en gros 640 Mo).

Citation :


/*
* Last Change: Mon Oct 30 01:00 PM 2006 J
*
* Small test to see pratical difference beween summing in 32 and summing in 64 bits
* on the platform where this program is run
*/

/*
* For ISO C, all floating functions are for double by default.
* If they have the suffix f, they are float
*/

/* For FLT_ constants */
#include <float.h>

/* for drand48 (SVID standart) and exit */
#include <stdlib.h>
/* for log10 */
#include <math.h>

#include <stdio.h>

int do_compare(size_t nelements, double dbd);
int generate_signals(double *in, double *in2, size_t nel, double dbdiff);

int lev2dB(double ref, double val, double* ret);
double db2coeff(double dblev);
double avdbdiff(const double* ref, const double* val, size_t nel);
double avdbref(const double* in, size_t nel, double ref);
double mindbref(const double* in, size_t nel, double ref);

long double power(const double*in, size_t nel);

int sum32(const float* in1, const float* in2, float* out, const size_t n);
int sum64(const double* in1, const double* in2, double* out, const size_t n);
int diff64(double* inout, const double *in2, size_t nel);

int double2float(const double* in, float *out, size_t nel);
int float2double(const float* in, double* out, size_t nel);

/* alloc or die */
void* xmalloc(size_t n);

/* Not sure about precision, but should not matter, since
* it is used only for db computation*/
double exp10(double in)
{
return exp(in * log(10));
}

/* Entry point */
int main(void)
{
int status;
size_t nel = 10000000;
double dbdiff = -80;

status = do_compare(nel, dbdiff);
if (status < -1) {
fprintf(stderr, "Error while comparing...n");
return -1;
}
return 0;
}

int do_compare(size_t nel, double dbd)
{
double *a, *b, *s, *s64;
float *a32, *b32, *s32;
int status;
double sumdbdiff;

/*
* Allocations
*/
a = xmalloc(sizeof(*a) * nel);
b = xmalloc(sizeof(*b) * nel);
s = xmalloc(sizeof(*s) * nel);
a32 = xmalloc(sizeof(*a32) * nel);
b32 = xmalloc(sizeof(*b32) * nel);
s32 = xmalloc(sizeof(*s32) * nel);
s64 = xmalloc(sizeof(*s64) * nel);

srand48(time(0));
/*
* Generate test signals
*/
status = generate_signals(a, b, nel, dbd);
if (status) {
fprintf(stderr, "%s:%s, %d errorn", __FILE__, __func__, __LINE__);
exit(EXIT_FAILURE);
}

/*
* Make a copy of test signals into 32 bits buffers
*/
status = double2float(a, a32, nel);
if (status) {
fprintf(stderr, "%s:%s, %d errorn", __FILE__, __func__, __LINE__);
exit(EXIT_FAILURE);
}
status = double2float(b, b32, nel);
if (status) {
fprintf(stderr, "%s:%s, %d errorn", __FILE__, __func__, __LINE__);
exit(EXIT_FAILURE);
}

/*
* Do the summation in 32 bits
*/
status = sum32(a32, b32, s32, nel);
if (status) {
fprintf(stderr, "%s:%s, %d errorn", __FILE__, __func__, __LINE__);
exit(EXIT_FAILURE);
}
status = float2double(s32, s64, nel);
if (status) {
fprintf(stderr, "%s:%s, %d errorn", __FILE__, __func__, __LINE__);
exit(EXIT_FAILURE);
}

/*
* Do the summation in 64 bits
*/
status = sum64(a, b, s, nel);
if (status) {
fprintf(stderr, "%s:%s, %d errorn", __FILE__, __func__, __LINE__);
exit(EXIT_FAILURE);
}

/*
* Compute the difference (in 64 bits) (in place into s)
*/
status = diff64(s, s64, nel);
if (status) {
fprintf(stderr, "%s:%s, %d errorn", __FILE__, __func__, __LINE__);
exit(EXIT_FAILURE);
}

/*
* Compare the results
*/
sumdbdiff = avdbref(s, nel, 1.0);
printf("average db diff between 32 bits summing and 64 summing is %fn", sumdbdiff);
sumdbdiff = mindbref(s, nel, 1.0);
printf("worse db diff between 32 bits summing and 64 summing is %fn", sumdbdiff);
#if 0
#endif

/* Those free are just for checking under valgrind */
free(a);
free(b);
free(s);
free(a32);
free(b32);
free(s32);
free(s64);

return 0;
}

/* compute the power of in: 1/nel * sqrt(sum(in ** 2)) */
long double power(const double*in, size_t nel)
{
long double acc = 0;
size_t i;

for(i = 0; i < nel; ++i) {
acc += in[i] * in[i];
}

return sqrtl(acc) / nel;
}

int diff64(double* inout, const double *in2, size_t nel)
{
size_t i;

for(i = 0; i < nel; ++i) {
inout[i] -= in2[i];
}

return 0;
}

/* Generate (uniform) random signals in and in2. in2 has dbdiff dB difference on average
* compare to in1, and in1 is normalized in the range [-1, 1] */
int generate_signals(double *in1, double *in2, size_t nel, double dbdiff)
{
size_t i;
double amp = db2coeff(dbdiff);

for(i = 0; i < nel; ++i) {
in1[i] = 2 * drand48() - 1;
in2[i] = amp * (2 * drand48() - 1);
}

printf("Asked level is %f, real level on average is %fn", dbdiff, avdbdiff(in1, in2, nel));

return 0;
}

/* Convert a and b content to 32 bits and put the results into a32 and b 32.
* I am not sure about the correct way to do the convertion... */
int double2float(const double* in, float *out, size_t nel)
{
size_t i;
for(i = 0; i < nel; ++i) {
out[i] = (float)in[i];
}

return 0;
}

int float2double(const float* in, double* out, size_t nel)
{
size_t i;

for(i = 0; i < nel; ++i) {
out[i] = (double)in[i];
}

return 0;
}

/* give the *average* db level compared to a reference 0 dB */
double avdbref(const double* in, size_t nel, double ref)
{
long double pow;
double dbl;

pow = power(in, nel);

lev2dB(ref, (double)pow, &dbl);

printf("ref is %10e, pow is %10e, level is %fn",
ref, (double)pow, dbl);

return dbl;
}

/* give the min db level compared to a reference 0 dB */
double mindbref(const double* in, size_t nel, double ref)
{
double maxval;
double tmp;
double dbl;
size_t i;

maxval = 0;
for(i = 0; i < nel; ++i) {
tmp = fabs(in[i]);
if ( tmp > maxval) {
maxval = tmp;
}
}

lev2dB(ref, maxval, &dbl);

printf("ref is %10e, max is %10e, level is %fn",
ref, maxval, dbl);

return dbl;
}

/* give the *average* db difference between two signals */
double avdbdiff(const double* ref, const double* val, size_t nel)
{
long double pow1;
long double pow2;
double dbl;

pow1 = power(ref, nel);
pow2 = power(val, nel);

lev2dB((double)pow1, (double)pow2, &dbl);

printf("pow1 is %10e, pow2 is %10e, dbdiff is %fn",
(double)pow1, (double)pow2, dbl);

return (dbl);
}

/* put the db value with reference level ref into ret
* return 0 on success, -1 otherwise */
int lev2dB(double ref, double val, double* ret)
{
if (fabs(val) < DBL_EPSILON || val < DBL_MIN) {
fprintf(stderr, "%s:%s, line %d, val (%f) is below epsilon or negativen",
__FILE__, __func__, __LINE__, val);
return -1;
}
*ret = 20*log10(ref / val);

return 0;
}

/* given a dB level difference, returns the amplitude coeff */
double db2coeff(double dblev)
{
return exp10(dblev / 20.);
}

/* sum in 32 bits */
int sum32(const float* in1, const float* in2, float* out, const size_t n)
{
size_t i;
for(i = 0; i < n; ++i) {
out[i] = in1[i] + in2[i];
}

return 0;
}

/* sum in 64 bits */
int sum64(const double* in1, const double* in2, double* out, const size_t n)
{
size_t i;
for(i = 0; i < n; ++i) {
out[i] = in1[i] + in2[i];
}

return 0;
}

void* xmalloc(size_t n)
{
void* tmp;
tmp = malloc(n);
if (tmp == NULL) {
fprintf(stderr, "Error while allocatingn");
exit(EXIT_FAILURE);
}
return tmp;
}



qui me donne encore une fois autour de 220 dB en moyenne et 141 dB dans le pire des cas sur 10^7 tirages. Je pourrais essayer plus si je me faisais chier a bufferiser au lieu de tout allouer en une fois, mais j'ai pas que ca a foutre non plus :)

Bref, a moins que je me sois plante quelque part (ce qui n'est pas impossible dans le code C, mais dans le code python, je pense pas trop), le fait que les calculs internes soient faits de toute facon en 80 bits sur un proc intel meme avec des valeurs d'entree/sortie en 32 bits fait qu'on garde bien une precision tres bonne avec du 32 bits, et ce meme dans un cas vraiment extreme (mixage de deux pistes avec 80 dB de difference).
144
En python, j'ai fait une autre experience: apres la sommation en 32 bits, j'ai recupere le signal de depart en enlevant la piste a 0 dB, histoire de voir ce qu'on perd en dynamique dans la sommation

Citation :



#! /usr/bin/env python
# Last Change: Mon Oct 30 01:00 PM 2006 J

import numpy as N

n = int(1e5)
dbd = -80

# A is double, between -1 and 1 (0dB)
A = 2*N.random.rand((n))-1
# B is double, between -1e-4 and 1e4
B = (2*N.random.rand((n))-1) * 10 ** (dbd/20)

S = A + B

powerA = N.sqrt(N.sum(A**2)) * 1./n
powerB = N.sqrt(N.sum(B**2)) * 1./n
print "Real db Diff (power) is %f" % (20*N.log10(powerA/powerB))

A32 = N.float32(A)
B32 = N.float32(B)

S32 = A32 + B32

# numpy broadcast to 64 bits automatically, so the difference is done
# in 64 bits
diff = S32 - S

powerdiff = N.sqrt(N.sum(diff ** 2)) * 1./n
maxdiff = N.max(N.abs(diff))

print "average diff of dynamic compared to bigger signal (0 dB) is %f" % (20*N.log10(powerdiff))
print "worse dynamic compared to bigger signal (0 dB) is %f" % (20*N.log10(maxdiff))

# Now, let's try to get back our little track b:
B32_2 = S32 - A32

diff = B32_2 - B32
# Check that the computation was done in 32 bits
assert diff.dtype == N.float32

powerdiff = N.sqrt(N.sum(diff ** 2)) * 1./n
maxdiff = N.max(N.abs(diff))

print "average diff of dynamic between b before and after summing %f" % (20*N.log10(powerdiff))
print "worse dynamic between b before and after summing %f " % (20*N.log10(maxdiff))



Et la encore, ca me donne a nouveau le resultat de 220 dB en moyenne, et 140 dans le pire des cas, en retrouvant la piste a -80 dB.

Concretement, ca veut dire qu'on aura une bonne chose d'avoir *exactement* les memes fichiers d'export a la fin en 24 bits (ca vaudrait le coup d'essayer, en fait) avec du summing 64 bits et 32 bits
145
Je vais verifier un peu mon programme, histoire de voir si je calcule pas n'importe quoi, et a la limite, je vais poster sur musicdsp et/ou LAD histoire de voir ce que des developpeurs bien mieilleurs que moi en pensent.
146

Citation : ce qui fait encore 60 dB sur le signal a -80 dB

Ben voilà, 60dB, ça fait combien de bits ?

JM
147
T'es vraiment un grand malade, Gabou :8O:

Mais bravo et merci :bravo2:
148

Citation :
Ben voilà, 60dB, ça fait combien de bits ?



Rien du tout, il y a pas de relation simple entre bits et dB, puisque ca depend de la representation ;) J'avais dit dans ma reponse du dessus que dans le cas du flottant, tu peux pas du tout utiliser la regle 1 bit supplementaire donne 6 dB de dynamique, car tu ne rentres pas du tout dans les hypotheses de la formule. Je rappelle que les hypotheses de cette formule, c'est:

- quantification *uniforme*
- bruit de quantification decorrele du signal

En plus, puisque les calculs intermediaires font de touue facon intervenir des registres a 80 bits (ou 64 si on utilise pas la fpu principale, mais la logique sse) sur intel, ce qui rend les calculs theoriques pour le moins compliques (c'est pour ca que j'ai programme le truc a la place).

Tu penses que mon raisonnement est correct sur les examples python ? Entre autre, je me demande si c'est vraiment significatif de retrouver le signal a -80 apres sommation et soustraction ? Je suis pas sur, en fait.
149

Citation : Attention, cette regle est totalement fausse en representation flottante, qui n'est pas lineaire du tout.

Absolument, je me place comme toi du point de vue du résultat final, soit en 24bits virgule fixe.
150
Concernant les programmes que tu as écrit, bravo, mais je suis une tanche dans ce domaine, 'n'vais pas pouvoir suivre sur ce terrain. Faut savoir admettre ses limites :noidea:

JM