[Thomson] Son 1 bit PWM en streaming

Cette catégorie traite de développements récents destinés à nos vieilles machines, applications, jeux ou démos... Amis programmeurs, c'est ici que vous pourrez enfin devenir célèbres!

Modérateurs : Papy.G, fneck, Carl

__sam__
Messages : 7925
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [Thomson] Son 1 bit PWM en streaming

Message par __sam__ »

En me basant sur la technique indiqué ici: https://en.wikipedia.org/wiki/Pulse-density_modulation, j'ai fait quelques essais avec le code suivant dans la boucle principaleen faisant varier DAMP en puissances de deux

Code : Tout sélectionner

$v = &echantillon_audio();
$out = $v >= $err ? 127 : -127;
$err += ($out - $v)/$DAMP;
&sortir($out>0 ? 1 : 0); 
DAMP joue sur la vitesse de l'intégrateur (plus petit il est, plus vite l'intégrateur réagit). A noter: l'intégration sur l'erreur se fait sur "sortie-entrée", alors que j'aurais spontanément écrit l'inverse "entrée-sortie" de sorte qui si la sortie dépasse l'entrée, le coup suivant un -127 est produit. Mais bon j'ai respecté la formule.

Je ne sais pas si l'émulateur est le meilleur outil pour évaluer la qualité audio: J'entends fort un sifflement continue indépendant de la musique quand DAMP est petit.

Ca me semble logique car a la base pour faire un signal constant à 50% de volume, le code va produire une alternance de 0/1. D'où le sifflement. En principe ce sifflement a une fréquence voisine de 31.25khz (moitié de la période du player), donc largement au dessus de la fréquence de coupure à 22khz. Mais comme l’émulateur marche en échantillonné il y a repliement du spectre. Le sifflement à 31.25khz est envoyé sur 22khz - (31.25-22Khz) = 12khz. Ca s'entend bien! (sur émulateur). Je pense que sur machine réelle le sifflement est fortement atténué.

Quand DAMP est petit, le système réagit plus vite aux changements de fréquences et si on oublie le sifflement, la qualité du son doit être très bonne.

A l'inverse quand DAMP est grand, on entendre de moins en moins le sifflement, mais on perd en qualité. Des distorsions se feront entendre.

Le truc est de trouver un DAMP convenable. Comme je ne fais pas confiance à l'émulateur à cause du repliement, Il faut entendre sur machine réelle. Voici des fichiers de test: http://www.cjoint.com/doc/15_08/EHztWOZhucM_CCR.zip Lequel ressort le mieux selon toi Daniel ?

[EDIT] A priori sur l'émulateur DAMP=16 convient pour avoir un son très correct. Voici quelques morceaux: http://www.cjoint.com/doc/15_08/EHzuXIg1CCM_PWM.zip
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17320
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [Thomson] Son 1 bit PWM en streaming

Message par Daniel »

De mon côté j'ai essayé d'ajouter un bruit blanc avant d'appliquer l'algorithme de diffusion d'erreur. Avec une amplitude faible du bruit, il y a peut-être une amélioration, mais peu perceptible, avec toujours les mêmes défauts un peu atténués. Si on augmente le bruit les défauts s'atténuent encore un peu mais le bruit devient de plus en plus audible. Pour résumer ce n'est pas très convaincant.

Les essais sur le vrai MO5 de DAMP1 à DAMP1024 donnent les résultats suivants :
- DAMP1 : pas de distorsion, petits grattements supportables
- DAMP1024 : distorsion insupportable, il y a peut-être moins de grattements mais il en reste quand le niveau du son est faible.

La distorsion commence à apparaître à DAMP4, elle devient difficile à supporter à partir de DAMP32.
Les grattements diminuent peut-être un peu mais ce n'est pas très convaincant.
Ceci dit, DAMP1 et DAMP2 sont plutôt meilleurs que mon premier essai.

Je crois que la seule bonne solution pour améliorer est d'augmenter la fréquence. Il faudrait pouvoir la doubler, mais je ne sais pas le faire avec un 6809 à 1MHz. Peut-être avec un 6309 ?

@sam : je t'ai envoyé un arduino et son interface USB pour que tu puisses tester directement.
Daniel
L'obstacle augmente mon ardeur.
Daniel
Messages : 17320
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [Thomson] Son 1 bit PWM en streaming

Message par Daniel »

J'ai écouté sur MO5 quelques morceaux du dernier fichier zip. Le son est propre (pratiquement pas de grattements ni de souffle), mais je crois qu'il y a quand même pas mal de distorsion. J'ai trop mauvaise oreille pour en être sûr, mais c'est mon impression. Il faut dire aussi que le MO5 génère un bruit de fond assez fort, et le téléviseur en ajoute un peu. On est loin de la qualité d'une chaîne HiFi, et dans ces conditions il est difficile de juger la qualité de l'algorithme.

L'émulateur dcmoto joue le son à 22050 Hz pour pouvoir fonctionner sur des PC lents. Aujourd'hui les machines sont plus rapides, je pourrais sans difficultés passer à 44,1 ou 48 kHz. Le signal du buzzer n'est pas pris instantanément lors de l'envoi d'un échantillon, il est intégré sur la période d'échantillonnage. C'est pourquoi le PWM à 62,5 kHz est à peu près réaliste pour l'oreille humaine. Mais dans l'opération il y a évidemment quelques arrondis, qui déforment légèrement le signal restitué.
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7925
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [Thomson] Son 1 bit PWM en streaming

Message par __sam__ »

Daniel a écrit : Les essais sur le vrai MO5 de DAMP1 à DAMP1024 donnent les résultats suivants :
- DAMP1 : pas de distorsion, petits grattements supportables
- DAMP1024 : distorsion insupportable, il y a peut-être moins de grattements mais il en reste quand le niveau du son est faible.
Dans l'émulateur, le son de DAMP1 est très faible et le sifflement très fort. Dans DAMP1024 le son est très fort et le sifflement très faible. Ok, c'est la preuve qu'il ne faut pas se fier à l'émul pour les son échantillonné au dessus de la fréquence d’échantillonnage de l'émulateur lui-même.

DAMP1 correspond au script suivant:

Code : Tout sélectionner

#/bin/perl

$file = $ARGV[0];
$name = $file; $name =~ s/\.[^\.]*$//; $name .= ".sd";
exit if -e $name;

$cycles = 16; # cyles cpu par echantillon audio
$hz  = int(2000000/$cycles+.5)/2;

# AUDIO: signed 8bits, mono
open(AUDIO, "./ffmpeg -i \"$file\" -v 0 -f u8 -ac 1 -ar ".int(2*$hz)." -acodec pcm_s8 - |");
binmode(AUDIO);

# fichier sd (sortie)
open(OUT, ">$name");
binmode(OUT);

# parametres et variables globales
$audio_corr = 0;
$lvl = 127;
$err = 0;

for($running = 1, $data = 1; $running; ) {
	my $v = &echantillon_audio();
	my	$out = $v >= $err ? $lvl : -$lvl;
	$err += ($out - $v);
	$data += $data + ($out>0?1:0);
	
	if($data & 256) {
		push(@buf, $data&255);
		$data = 1;
		if($#buf==511) {
			print OUT pack('C*', @buf);
			@buf = ();
		}
	}
}
while(($data & 256)==0) {$data +=$data;}
push(@buf, $data&255);
print OUT pack('C*', @buf);

# fermeture flux
close(OUT);
close(AUDIO);

# Lit 2 échantillons audio de FFMPEG et le normalise.
sub echantillon_audio {
   if(!@AUDIO) {
	  my($buf);
	  $running = 0 if !read(AUDIO,$buf,8192);
	  push(@AUDIO, unpack('c*', $buf));
   }
   my $v = (shift(@AUDIO)+shift(@AUDIO))/2;
   $audio_corr = abs($v) if abs($v)>$audio_corr;
   return $v*$lvl/(1+$audio_corr);
}
@sam : je t'ai envoyé un arduino et son interface USB pour que tu puisses tester directement.
Merci. Je suis en vacance à 800km, mais je verrais cela à mon retour semaine prochaine. Suite par mail.
J'ai écouté sur MO5 quelques morceaux du dernier fichier zip. Le son est propre (pratiquement pas de grattements ni de souffle), mais je crois qu'il y a quand même pas mal de distorsion. J'ai trop mauvaise oreille pour en être sûr, mais c'est mon impression.
Oui les distorsions sont possibles car les fichiers sont fait avec DAMP=16. Je vais les refaire avec le script ci-dessus pour voir.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
__sam__
Messages : 7925
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [Thomson] Son 1 bit PWM en streaming

Message par __sam__ »

__sam__ a écrit :Je vais les refaire avec le script ci-dessus pour voir.
Je les ai refaits: http://www.cjoint.com/doc/15_08/EHAiZSOP0qM_PWM.zip

Première constatation le zip est 30% plus petit: il est passé de 15Mo à 10Mo! Les fichiers se compressent bien mieux. A tel point que CCR fait autour de 512Ko. Il tiendrait dans la ram, mais il faudrait le décompresser en temps réel.

2eme constatation: il peut y avoir des grésillements au début le temps qu'une période de forte amplitude s'installe dans les premières secondes. C'est lié à l'auto-correction du niveau sonore dans le script permettant d'avoir des morceaux de même volume quel que soit le volume de la source.
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17320
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [Thomson] Son 1 bit PWM en streaming

Message par Daniel »

J'ai surtout écouté CCR, car je connais bien l'original. C'est très bon. Je ne retrouve plus les défauts constatés dans ma première version, ou alors ils sont très atténués. Le bruit de fond est recouvert par celui du MO5 et du téléviseur, on ne l'entend pratiquement pas. Le tout début est un peu surprenant à cause du contrôle automatique de gain, et je crois qu'il y a une légère distorsion dans les sons les plus aigus. Mais je ne sais pas quelle version originale tu as utilisée.

Pour mes tests, j'ai utilisé comme original un fichier .wav stéréo 32 bits à 88200 échantillons par seconde.
Comme il n'est pas enregistré très fort, je l'ai transformé en mono 8 bits en ajoutant 100% du canal gauche et 100% du canal droit.
Fichier .wav original : http://www.filedropper.com/ccrwav

[Edit] Après plusieurs écoutes, le plus gros défaut est une forte atténuation des basses, perceptible en particulier pour la batterie. Dans la version PWM elle a un son métallique, alors que l'original a des basses plutôt puissantes.
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7925
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [Thomson] Son 1 bit PWM en streaming

Message par __sam__ »

Daniel a écrit :Pour mes tests, j'ai utilisé comme original un fichier .wav stéréo 32 bits à 88200 échantillons par seconde.
Comme il n'est pas enregistré très fort, je l'ai transformé en mono 8 bits en ajoutant 100% du canal gauche et 100% du canal droit.
Fichier .wav original : http://www.filedropper.com/ccrwav
Bon, c'est cool, j'ai découvert qu FFMPEG sait aussi lire des WAV, du coup je peux appliquer le script sur le même fichier source que le tiens.

Ca donne le fichier "ccr1.sd". Pour le fichier "ccr2.sd" je me suis basé sur le filtre "noise shaper" d'ordre deux d'ici : http://class.ece.iastate.edu/cpre583/pr ... report.pdf (page 2).

Code : Tout sélectionner

my $v = &echantillon_audio();
$v += $d2*-1;
$v += $d1*2;
$out = $v>0?$lvl:-$lvl;
$d2 = $d1; # retard
$d1 = $v - $out; # retard
http://www.cjoint.com/doc/15_08/EHAnSgEvvnM_ccr.zip

Avec ta version à 88khz le bruit est différent de la versions CCR que j'avais extraite d'un video clip. Il est aussi légèrement différent entre les deux filtres, mais sans pouvoir déterminer lequel est le mieux (à cause de la porteuse lié à l'échantillonnage de l'émulateur). J'ai fait une analyse de spectre avec audacity (enregistrement à 176khz, histoire de ne pas avoir de repliement par l'échantillonnage d'Audacity) entre ccr1.sd et ccr2.sd. Le spectre est similaire sans être strictement identique. Dans les deux cas il y a un pic à 8khz sur la version jouée par l'émulateur:
Sans titre.png
Sans titre.png (25.34 Kio) Consulté 4688 fois
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17320
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [Thomson] Son 1 bit PWM en streaming

Message par Daniel »

Dans les deux versions ccr1 et ccr2 le rendu est excellent : pas de distorsion audible et bonne bande passante. Je retrouve le son de batterie de l'original.

Dans ccr1 présence de grésillements et de grattements, pas très forts mais gênants.
Dans ccr2 absence presque totale de bruits parasites, autant que je peux en juger avec le bruit de fond élevé du MO5.

Le seul problème est le niveau faible des deux versions. Il nécessite d'amplifier beaucoup, et on entend d'autant plus le bruit de fond de l'installation. J'ai déjà essayé une alimentation régulée et une mise à la terre, mais ça ne supprime rien. Je crois que la conception de la carte mère, sans aucun blindage pour les signaux audio, ne permet pas de faire mieux.

Il faudrait envoyer le signal du buzzer directement vers un ampli extérieur, sans passer par la Péritel, et surtout sans le mixer avec la sortie du CNA. Elle est au repos, puisque les 6 bits sont initialisés en entrée, pourtant je suis persuadé qu'elle participe à la génération du bruit. Il faut que j'essaye sur TO8, mais j'ai peur que ce soit pareil, ou même pire. Le signal MUTE du TO8 n'est pas traité comme le BEEP du MO5 (voir les explications plus haut), et ça ne doit rien arranger.

En dehors de ces considérations électroniques, on peut dire que ccr2 est une réussite. Pour l'améliorer il faudrait pouvoir obtenir un niveau plus élevé.
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7925
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [Thomson] Son 1 bit PWM en streaming

Message par __sam__ »

Si ccr2 est mieux c'est probablement lié à l'ordre du filtre. Il est d'ordre 2 (deux retards). Le PDF poursuit avec des filtres encore plus complexes: ordre 6 page 11:
Sans titre.png
Sans titre.png (31.39 Kio) Consulté 4653 fois
qui est nettement meilleur d'après les FFT du document (à la louche au moins -10db d'écart entre les deux sur le bruit blanc et sensiblement identique sur le signal). Il y aurait donc 10x moins de bruit avec le filtre d'ordre 6 (en fait ce sont 3 filtres "noise-shaper" d'ordre 2 qui sont combinés.) Cependant la sortie n'a pas 2 niveaux, mais 2^n avec n=nb de "noise-shaper", donc ici 2^3 = 8 niveaux. Ca ne convient pas. pour notre flux binaire. Mieux vaut rester au filtre de niveau 2:

Code : Tout sélectionner

#/bin/perl

$file = $ARGV[0];
$name = $file; $name =~ s/\.[^\.]*$//; $name .= ".sd";
exit if -e $name;

$cycles = 16; # cyles cpu par echantillon audio
$hz  = int(2000000/$cycles+.5)/2;

# AUDIO: unsigned 8bits, mono, 16125hz
open(AUDIO, "./ffmpeg -i \"$file\" -v 0 -f u8 -ac 1 -ar ".int(2*$hz)." -acodec pcm_s8 - |");
binmode(AUDIO);


# fichier sd (sortie)
open(OUT, ">$name");
binmode(OUT);

$audio_corr = 64;
$lvl = 127;

for($running = 1, $data = 1; $running; ) {
	my $v = &echantillon_audio();
	
	if(1) { # ordre 2
		$v += $d1+$d1-$d2;
		$out = $v>0 ? $lvl:-$lvl;
		$d2 = $d1;
		$d1 = $v - $out; 
	} else { # ordre 1
		$out = $v >= $err ? $lvl : -$lvl;
		$err += ($out - $v); 
	}
	
	$data += $data + ($out>0?1:0);
	
	if($data & 256) {
		push(@buf, $data&255);
		$data = 1;
		if($#buf==511) {
			print OUT pack('C*', @buf);
			@buf = ();
		}
	}
}
while(($data & 256)==0) {$data +=$data;}
push(@buf, $data&255);
print OUT pack('C*', @buf);

# nettoyage et fermeture flux
close(OUT);
close(AUDIO);

# Lit 2 échantillons audio de FFMPEG et le renormalise
sub echantillon_audio {
   if(!@AUDIO) {
	  my($buf);
	  $running = 0 if !read(AUDIO,$buf,8192);
	  push(@AUDIO, unpack('c*', $buf));
   }
   my $v = (shift(@AUDIO)+shift(@AUDIO))/2;
   if(abs($v)>$audio_corr) {
	 $audio_corr = abs($v);
	 print "AUDIO_MAX = $audio_corr\n";
   }
   return $v*$lvl/(1+$audio_corr);
}
C'est assez magique ces filtres numériques finalement.

[EDIT] oulà, ce filtre me fait un truc bizzare dans les premières secondes de ce morceau http://www.cjoint.com/doc/15_08/EHAuI68 ... -on-me.zip. Je ne m'explique pas d'où ca sort surtout que ca disparait aussi vite que ca apparait.)

[EDIT2] c'est surement une question de niveaux d'entrée trop haut qui cumulé avec l'intégration de l'erreur ne peut plus être compensée pendant un certain temps. C'est vraiment étrange mais la modif suivante évite ce soucis

Code : Tout sélectionner

# Lit 2 échantillons audio de FFMPEG et le met à un niveau bien plus petit que $lvl.
sub echantillon_audio {
   if(!@AUDIO) {
	  my($buf);
	  $running = 0 if !read(AUDIO,$buf,8192);
	  push(@AUDIO, unpack('c*', $buf));
   }
   my $v = (shift(@AUDIO)+shift(@AUDIO))/2;
   if(abs($v)>$audio_corr) {
	 $audio_corr = abs($v);
	 print "AUDIO_MAX = $audio_corr\n";
   }
   return $v*120/(1+$audio_corr); # 120 au lieu de 127 ($lvl)
}
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
__sam__
Messages : 7925
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: [Thomson] Son 1 bit PWM en streaming

Message par __sam__ »

Je me pose une question existentielle. On fait "COM <$CD " pour inverser le bit d'ack ce qui prend 6 cycles: c'est lent! Comme <$CC (PORTA) est en entrée, il ne se passe rien si on écrit dedans le pense. Du coup on pourrait faire STX <$CC et STU <$CC pour passer à 1 ou à 0 le bit d'ack (X et U sont initialisés avec les bonnes valeurs). On gagne 1 cycle, et le nombre de cycle d'une étape d'extraction de bit passe de 16 cycles à 15 cycles.

Ca ne semble pas super avantageux de passer à 15 cycles (66667khz vs 62500), mais 66667 est très proche de 3*22050, c'est à dire que l'on joue au niveau de la fréquence d'échantillonnage de l'émulateur, ce qui peut avoir un impact sur le sifflement: 3*22khz nous donne un sifflement à 33khz, qui est renvoyé à 22-(33-22) = 11khz. Or il semble que l'émulateur a une fréquence de coupure à 10khz, juste en dessous. Donc avec un peu de chance le sifflement sera un peu atténué sur l'émulateur...

A tester (car je ne suis pas 100% sur. Je n'ai pas sérieusement travaillé sur les histoires de repliement de spectre depuis mes études, au siècle passé ;) ).
Samuel.
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Daniel
Messages : 17320
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: [Thomson] Son 1 bit PWM en streaming

Message par Daniel »

Dans la boucle de traitement d'un octet, il y a un BRA PLAY et un LDA <$CC, soit 7 cycles incompressibles. Avant de jouer le premier bit de l'octet, on attend donc 7 cycles.

Il faudrait aussi attendre 7 cycles pour les autres bits. J'ai triché un peu, en mettant 6 cycles pour les bits 1 à 7 : COM <$CD pour les bits 1 et 2, trois NOP pour les suivants. Peut-on jouer les bits de façon irrégulière, en remplaçant le COM <$CD par une instruction plus courte et en supprimant les NOP ? Je n'ai pas essayé.

Mais il est pratiquement sûr qu'on peut le faire, si on en tient compte dans le programme de génération du fichier SD. On peut aussi remplacer l'acknowledge sur front montant par un acknowledge sur front alterné, comme dans SDANIM7. On gagne alors 6 cycles sur les 6 derniers bits, on passe ainsi de 129 cycles à 93 cycles par octet, soit une fréquence moyenne de 86022 Hz par bit.

Je ne sais pas si l'amélioration sera audible. Dans le fichier pdf, ils écrivent que la fréquence habituelle pour le PWM est aux environs de 16 MHz, alors nous sommes encore très loin du compte. Il y a sûrement une corrélation entre la fréquence PWM et la bande passante. Je crois aussi qu'en augmentant le niveau du signal on augmente les distorsions. D'un autre côté, en diminuant le niveau, on augmente le rapport bruit/signal. Tout est parfaitement logique, il n'y a pas de miracle.

[Edit 1]
Une idée d'amélioration pour les 7 cycles "incompressibles" : on peut faire le BRA PLAY après avoir joué le bit 6, jouer le bit 7 au début de la boucle, puis lire l'octet suivant. On remplace ainsi les 7 cycles d'attente sur 1 bit par 3 cycles sur 1 bit et 4 cycles sur le suivant.

[Edit 2]
L'essai avec Eric Prydz me fait penser aux systèmes électroniques de régulation. Quand on augmente le gain pour améliorer la précision de la régulation, il arrive un moment où le système se met à auto-osciller, et alors il n'y a plus aucune régulation. Il faut trouver le bon réglage, juste avant l'auto-oscillation.

[Edit3]
Pour éviter le bruit généré par la carte mère sur la sortie son Thomson, on peut sortir le bit PWM sur le bit 7 de <$CD. Et ainsi, on joue le son et on complémente le bit de synchronisation en une seule instruction. C'est un gain de 4 cycles par bit et 32 cycles par octet, pour passer à 131148 Hz, si je ne me suis pas trompé dans les calculs.
La sortie TTL peut être adaptée pour l'entrée ligne d'une enceinte amplifiée, style moniteur de studio. On gagne nettement en qualité, mais on perd un peu l'authenticité de l'application Thomson. A la limite, on peut laisser l'Arduino seul et enlever le MO5 :wink:
Daniel
L'obstacle augmente mon ardeur.
Répondre