HectorDuino

Placez ici vos trucs et astuces, étalez sans retenue votre savoir-faire et votre science qui va nous permettre de redonner une apparence neuve et fonctionnelle à nos bouzes.

Modérateurs : Papy.G, fneck, Carl

Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

C'est pas faux, et effectivement j'ai un meilleur son sur la machine réelle.
Sur l'émulateur je suis à 44,1khz pour le son. j'avais utilisé une fréquence assez élevée pour être sur de ne pas avoir de soucis de ce type là (et je pensais être à l'abri de ça car pour moi le repliement était à 22khz et non à (44-X/2) khz et (44-X) khz...
A la limite, je pourrais peut être modifier l'ému pour passer un 88.2Khz ?

Bref, cela ne m'explique quand même pas pourquoi un son filtré ordre 1 est meilleur qu'ordre 2 :? je reste perplexe.
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

J'ai donc travaillé un peu dessus et j'ai passé le fichier son joué en interne dans VBHector de 44,1KHz à 88.2 KHz et j'ai fini à 176,4KHz !

Cela ne ralenti en rien l'émulation, mais la synchro entre les cycles Z80 et la création d'un échantillon son se réduit comme peau de chagrin et donc l'erreur plus importante.
Par contre, et là c'est bien, le son devient comme par magie aussi bien que sur la machine réelle ! c'est bô les math !

Pour l'utilisation en player 1 bit c'est beaucoup mieux mais les sons 'classiques' (sn76477, 1bit dans les jeux etc...) deviennent plus problématique. Je pense mettre une option au démarrage 44,1KHz / 176,4KHz pour laisser le choix selon le test que l'on veut faire.
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

Ce que tu peux faire, c'est de toujours travailler à 44khz, mais tu n'envoie pas les échantillons tels quels vers l'audio. Tu les fait passer par un filtre passe-baseà 22khz.. comme ca les fréquences qui se replieront seront d'intensité rikiki, et le son sera de très bonne qualité.

Il doit y avoir plein de pointeurs pour pour programmer un FIR passe-bas 22khz avec un échantillonnage à 44khz. Par exemple avec: http://www.digitalfilter.com/products/d ... dfalz.html
Untitled.png
Untitled.png (28.39 Kio) Consulté 5082 fois
Untitled2.png
Untitled2.png (52.03 Kio) Consulté 5082 fois
(bon il y a 90 coefs.. il faut scroller :) )
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
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

tu as raison sam, mais attention quand même, mon émulateur est écrit en VB Net et pas en C; c’était mon postulat de départ pour voir les capacités du VBNet... Actuellement lorsque je lance une émulation Hector + Arduino en video ou son je suis à près de 100% d'utilisation d'un thread de mon processeur. Ajouter trop de math est un problème.
Par contre j'ai trouvé un dernier index qui était en hard dans l'émulation son qui fait que maintenant même à 176,4KHz les sons sont correctes ! (sn76477, MEA8000 et 1 bit !!)

j'ai donc laissé à 176.4KHz pour tout le monde !

Remarque le filtre semble assez simple à réaliser...
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

un filtre FIR c'est pas trop de maths: c'est un buffer cyclique sur les N dernier échantillons audio, puis on fait une somme pondérée pour sortir la valeur. Grosso-modo un truc comme ca:

Code : Tout sélectionner

.. globales
 const N = taille
 const coef[] = { .... };
 var tab[N], index;

 ... code (local)
 tab[index] = echantillon_non_filtré;
 var v = 0
 for i = 1 to N
    v = v+ tab[(index+i) mod N]*coef[i]:
 next
 index = (index + 1) mod N
 echantillon_filtré = v
 sortir(echantillon_filtré)
Après il y a des filtres meilleurs que d'autre. Les FIR font des osciilations dans la partie "plate" du spectre. Butterworth est mieux de ce point de vue.

J'ai pense, si ca se trouve l'api audio de windows fait déjà le boulot. Faudrait demander à Daniel comment est fait le filtre pour DCMoto. Codé en dur ou API simple à utiliser ?
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 : 17316
Inscription : 01 mai 2007 18:30
Localisation : Vaucluse
Contact :

Re: HectorDuino

Message par Daniel »

Dans dcmoto le signal est intégré pendant la période d'échantillonnage pour calculer l'échantillon à jouer, à la fréquence de 22050 échantillons par seconde. Il n'y a pas de filtre supplémentaire. C'est probablement le processus d'intégration qui limite les fréquences aigües. Avec un échantillonnage à 22050Hz on doit avoir une bande passante limitée à 11025Hz dans les aigus.
Daniel
L'obstacle augmente mon ardeur.
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

Oui, un intégrateur est un filtre linéaire. C'est vrai que le filtrage est un plus quand on joue aux fréquences supérieures ou égales à la moitié de celle de restitution. Avec les 60khz du dither 1 bit on est vraiment dans ces conditions.
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
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

Salut à tous,
Juste un petit mot pour vous dire qu'une nouvelle version (3.0) de VBHetor est disponible sur le site d'Yves (merci Yves !).

Cette version intègre quelques nouveauté dont, notamment, l'intégration que mes utilitaires (création de fichiers Mini disque, création des vidéos et audios pour Hectorduino, création de wav à partir de fichier K7) ainsi que l'émulation complète de la platine HectorDuino permettant de jouer les sons et les vidéos visibles sur Hector+HectorDuino.
Pas mal de petites choses ont été reprises comme par exemple la synchro du son avec DirectX (beaucoup plus fine, maintenant un "TONE 300,100" en basic est comme sur la vrai machine, sans vibrato ou déformation), la scrutation du Z80 également reprise (sans impacts visible mais plus clair dans le programme), ...
J'ai également été plus clair dans les remerciements (dans l'à propos) pour être plus sûr des licences des softs utilisés (ffmepg, tniasm et la reprise du module modZ80 du projet VB81)

l'évolution majeure étant quand même la possibilité de visualiser les vidéo de l'HectorDuino !

Have fun !
JJ

PS : évidement il est possible que de bugs apparaissent, n'hésitez pas à me les décrire ici même !


[EDIT] avec un lien :
http://hectorvictor.free.fr/index.php?page=8BMh79AVpFsJ
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

__sam__ a écrit : 06 oct. 2015 13:50

Code : Tout sélectionner

#/bin/perl
# Conversion video en gif 4 couls
#
# Samuel DEVULDER, Sept-Oct 2015.
#

# parametres
($W, $H) = (204, 153); # HR
$fps     = 10;
$dither  = "vac8";
($RED,$GRN,$BLU) = (2,4,1);

# fichier entree
$file    = $ARGV[0];

$out = $file;
$out =~ s/\.[^\.]*$/.gif/;
exit if -e $out;

($x,$y, $aspect_ratio) = (160,100,"16:9");
open(IN, "./ffmpeg -i \"$file\" 2>&1 |");
while(<IN>) {
	if(/, (\d+)x(\d+)/) {
		($x,$y) = ($1, $2);
		# 4:3
		if(abs($x - 4/3*$y) < abs($x - 16/9*$y)) {
			($w,$h,$aspect_ratio) = (133,100,"4:3");
		}
	}
}
close(IN);
$h = int(($w=$W)*$y/$x);
$w = int(($h=$H)*$x/$y) if $h>$H;
print $file," : ${x}x${y} ($aspect_ratio) -> ${w}x${h}\n";

# dossier temporaire
mkdir "tmp";
open(OUT,"| ./ffmpeg -i - -v 0 -r $fps -s ${w}x${h} -an tmp/img%05d.bmp");
open(IN, "<$file");

&init_magick;
$gif = Image::Magick->new(size=>"${w}x${h}");
	
$cpt = 1; my @c = (0)x8;
binmode(IN);
binmode(OUT);
while(1) {
	$name = sprintf("tmp/img%05d.bmp", $cpt);
	
	$expected_size = $h*(($w*3 + 3)&~3) + 54 if !$expected_size;
	if($expected_size != -s $name) {
		# image pas complete: on continue de nourrir ffmpeg
		my $buf;
		my $read = read(IN,$buf,4096);
		last unless $read;
		syswrite OUT, $buf, $read;
	} else  {
		# image complete!
		print STDERR int($cpt++/$fps),"s\r";
		sleep(5) if ($cpt%1200)==0; # on fait une pause régulière pour ne pas surchauffer le processeur
		
		# lecture de l'image
		my $img = Image::Magick->new();
		$img->Read($name);
		unlink $name;
		
		# on force la saturation (140%) pour avoir des couleurs plus franches
		$img->Modulate(saturation=>140);
		$img->Evaluate(operator=>'Multiply', value=>255/245);
		
		my $tmp = Image::Magick->new(size=>"${W}x${H}");
		$tmp->Read("xc:black");
		$tmp->Composite(image=>$img, Operator=>"Over", x=>($W-$w)>>1, y=>($H-$h)>>1);
		undef $img; $img = $tmp;
		my $orig = $img->Clone();

		# trammage
		$img->Set(colorspace=>$LINEAR_SPACE);
		$img->OrderedDither($dither);
		
		if(0) {
			# version R,G,B
			$img=$img->Fx(expression=>"(i+j)%3==0?r:0", channel=>"Red");
			$img=$img->Fx(expression=>"(i+j)%3==1?g:0", channel=>"Green");	
			$img=$img->Fx(expression=>"(i+j)%3==2?b:0", channel=>"Blue");	
		} else {
			# détermination des 4 couleurs les plus fréquentes (histograme + passe-bas)
			my $z = $orig;
			
			if(!defined $gauss) {
				$gauss = Image::Magick->new(size=>"${W}x${H}");
				$gauss->Read("xc:black");
				$gauss=$gauss->Fx(expression=>"exp(-8*(((i-$W/2)/$W)^2 + ((j-$H/2)/$H)^2))", channel=>"All");
				$gauss->Write("gauss.png");
			}
			$z->Composite(image=>$gauss, Compose=>"Multiply", channel=>"All");
			$z->Blur(sigma=>2);
			$z->Set(colorspace=>$LINEAR_SPACE);
			$z->OrderedDither($dither);
			$z->Write("gauss.png");
			
			my @h = $z->Histogram();
			my @H = (0)x8;
			for(my $i=$#h+1; ($i-=5)>=0;) {
				$H[($h[$i]&$RED)|($h[$i+1]&$GRN)|($h[$i+2]&$BLU)] = 
				$h[$i+4];
			}
			my @sf;
			for my $i (0..7) {
				my $t = int($c[$i] = ($c[$i]*0 + 32*$H[$i])/32);
				my $x = $t;	$x |= $x>>1; $x |= $x>>2; $x |= $x>>4; $x |= $x>>8; $x |= $x>>16; ++$x;
				my $y = $x>>4; $x -= $y?$y:1; $t &= $x;
				$sf[$i]=$t*16 + ($i==0?8:$i);
			}
			@h = sort {$sf[$b]<=>$sf[$a]} (0..7);
			# print "\n";
			# for my $i (@h) {
				# print $i, "=>", $sf[$i]>>4, " ",int($c[$i]), " (",$H[$i],")\n";
			# }
			
			
			# construction d'une palette avec ces 4 couleurs
			my $k = join(',', @h[0..3]);
			my $m = $pal{$k};
			if(!defined $m) {
				my(@px);
				for my $c (@h[0..3]) {
					push(@px, ($c&$RED)?255:0, ($c&$GRN)?255:0, ($c&$BLU)?255:0);
				}
				my $tmp = $ENV{'HOME'}."/.toto.pnm";
				open(ZZ,">$tmp");
				print ZZ "P6\n4 1\n255\n", pack('C*',@px),"\n";
				close(ZZ);
				$m = Image::Magick->new();
				$m->Read($tmp);
				unlink($tmp);
				$pal{$k} = $m;
			}
			
			# remping de l'image tramée sur ces 4 couleurs
			$img->Remap(image=>$m, 'dither-method'=>'none');
		}
		$img->Write("toto.png");
		
		# ajout de l'image au gif animé
		$img->Set(dispose=>"None");
		$img->Set(delay=>int(100/$fps));
		push(@$gif, $img);
		
		# pas plus de 3600 imgs (6mins)
		last if $cpt==4200;
	}
}
close(OUT);
unlink(<$ENV{'HOME'}/img*.bmp>);

# ecriture du fichier gif
$gif->Set(dispose=>"None");
$gif->Set(Layers=>"optimize-trans");
$gif->Set(delay=>int(100/$fps));
$gif->Write($out);

sub init_magick {
	# chargement image-magick la 1ere fois
	my($home) = "tmp";
	$ENV{'HOME'} = $home;
	mkdir($home);
	mkdir("$home/.magick");
	open(THR, ">$home/.magick/thresholds.xml");
		
	print THR <<EOF;
		<thresholds>
			<threshold map="2x2">
				<description>2x2 dither matrix</description>
				<levels width="2" height="2" divisor="5">
				1 2
				3 4
				</levels>
			</threshold>
			<threshold map="3x3">
				<description>3x3 dither matrix</description>
				<levels width="3" height="3" divisor="10">
				7 8 2
				6 9 4
				3 5 1
				</levels>
			</threshold>
			<threshold map="5x3">
				<description>5x3 dither matrix</description>
				<levels width="3" height="5" divisor="16">
				 3  9  4
				 8 14 10	
				13 15 11
				 7 12  5
				 2  6  1
				</levels>
			</threshold>			
			<threshold map="vac8">
				<description>void and cluster 65 niveaux</description>
				<levels width="8" height="8" divisor="65">
				35 57 19 55  7 51  4 21
				29  6 41 27 37 17 59 45
				61 15 53 12 62 25 33  9
				23 39 31 49  2 47 13 43
				 3 52  8 22 36 58 20 56
				38 18 60 46 30  5 42 28
				63 26 34 11 64 16 54 10
				14 48  1 44 24 40 32 50
				</levels>
			</threshold>
		</thresholds>
EOF
	close(THR);
		
	eval 'use Image::Magick;';
	
	# determination de l'espace RGB lineaire
	my $img = Image::Magick->new(size=>"256x1", depth=>16);
	$img->Read('gradient:black-white');
	$img->Set(colorspace=>'RGB');
	#$img->Set(colorspace=>"Gray") unless $coul;
	my @px1 = $img->GetPixel(x=>128, y=>0);
	$img->Read('gradient:black-white');
	$img->Set(colorspace=>'sRGB');
	#$img->Set(colorspace=>"Gray") unless $coul;
	my @px2 = $img->GetPixel(x=>128, y=>0);
	my $d1 = $px1[0]-0.5; $d1=-$d1 if $d1<0;
	my $d2 = $px2[0]-0.5; $d2=-$d2 if $d2<0;
	$LINEAR_SPACE = $d1>=$d2 ? "RGB" : "sRGB";
	#print $px1[0], "   ",$px2[0],"    $LINEAR_SPACE\n";
}
Sam, j'ai toujours eu un problème avec ton script que je ne comprend pas trop. Les notions de Perl m'étant toujours pas très familière.

Là où je bloque c'est dans le while(1) de la boucle de travail :
lorsque la vidéo dure moins longtemps que la limite dans le last (4200), les lignes après la fin du bloc du while ne sont pas exécutées et c'est pour ça que GIF ne se réalise pas.
Par contre je ne comprend pas comment on sort de ce while(1) en fin de traitement (lorsque toutes les images ont été traitées) et donc pourquoi on n'exécute pas la fin du script...

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

Re: HectorDuino

Message par __sam__ »

on sorte du while(1) avec

Code : Tout sélectionner

my $read = read(IN,$buf,4096);
last unless $read;
cad qu'on sort (last), sauf si (unless), on a lu quelque chose ($read est impliciment non vide), cad lorsqu'on a atteint la fin de fichier source (IN).
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
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

salut sam,
il me semblait aussi mais en mettant des print partout je ne comprend pas pourquoi lorsque l'on sort avec ce last on ne continu pas après le bloc : j'ai ajouté un print ici :

Code : Tout sélectionner

		last if $cpt==3600;
		}
}
print "fini !\n";
close(OUT);
unlink(<$ENV{'HOME'}/img*.bmp>);
et le "fini" n’apparaît pas si le film fait plus de 3600 images (et donc pas de GIF créé).

Question : "LAST" permet de quitter la boucle while(1) mais immédiatement (comme en c) où il laisse le bloc continuer et sort uniquement lors du rebouclage du while ?
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

le "last" du perl est equivalent du "break;" en C: il quitte immédiatement la boucle dans laquelle il est appellé.

Si le print ne s'affiche pas, ca peut-être causé par un problème de bufferisation de la sortie.

Je me demande si je ne devrais pas transformer mes scripts perl en scripts LUA. Etant donné que lua est bien plus facile à installer que perl (l'interpréteur fait quelques centaines de ko seulement.)
Dernière modification par __sam__ le 23 sept. 2017 20:16, modifié 1 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
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

ben c'est assez bizarre que selon le last utilisé (celui du read et celui du test de longueur) le print fonctionne ou pas...
de plus si je suis la dessus c'est que le GIF ne se créé pas si on sort par le read et il se créé si on sort par la limite du compteur...
__sam__
Messages : 7923
Inscription : 18 sept. 2010 12:08
Localisation : Brest et parfois les Flandres

Re: HectorDuino

Message par __sam__ »

ce qu'il serait possible c'est d'utiliser une variable "$ok" initialisée à 1 et l'on sort si $ok passe à 0.

Code : Tout sélectionner

$ok = 1
while($ok) {
	...
	$ok = 0 unless $read; # était: last unless $read;
	...
	$ok = 0 if $cpt==3600; # était: last if $cpt==3600;
	...
}
print "fini!\n";
close(OUT);
...
Mais un truc à faire avant tout ca: passer le paramètre du READ de 4096 à 256. En effet il est possible que le fichier soit si petit que dans la lecture de 4096 on lise l'équivalent de toutes les images restantes, empêchant d'atteintre $cpt==3600. C'est à essayer du moins.
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
Avatar de l’utilisateur
yo_fr
Messages : 1336
Inscription : 13 août 2009 18:24
Localisation : 78...
Contact :

Re: HectorDuino

Message par yo_fr »

Salut,

J'ai modifié le script avec la variable pour le while, ainsi que le buffer à 256. Pareil.
J'ai un doute sur le syswrite. J'ai fait un :

Code : Tout sélectionner

syswrite OUT, $buf, $read  if ($ok==1);
(c'est cocasse cette écriture!)

Mais c'est pareil : pas de suite si la sortie se réalise suite à la fin du traitement et correct si on réalise le LAST du nombre de frames.

il me semble que le : $ok = 0 unless $read;
ne fonctionne pas : Je fais un print $ok après cette ligne et même en fin de film le ok est à 1...

Edit :
pour moi le retour de read est le nombre d'octets soit 256. C'est d'ailleurs ce que je trouve en faisant un print $read qui me donne 256, même au dernier read, ce qui ne permet pas de sortir. Le "syswrite" planterait alors sur un buffer vide... ?
Répondre