Rebol le nouvel émulateur Old school ?
guest230-Jan-2007/19:40:52+1:00
A l'origine était le 'verbe' , puis Rebol est apparu...

Bon sérieux, à l'origine je voulais développer des outils en Rebol pour développer des jeux sur MSX.
Le MSX est une machine 8bit des années 80 chère à mon coeur.
D'ailleurs si le coeur vous en dit, vous pouvez tester la bête avec de zolis émulateurs comme BlueMsx et OpenMSX.
Donc j'avais dans l'idée d'écrire des outils pour développeur en assembleur sur proceseur Z80.
Il y'a 3 semaines, j'ai donc commencé à écrire un parseur minimaliste pour émuler du code Z80 désassemblé.

En gros à partir de quelque chose comme ça:

#4017:
4017 2100e0 ld hl,#e000
401a 1101e0 ld de,#e001
401d 01fe0f ld bc,#0ffe
4020 3600 ld (hl),#00
4022 edb0 ldir
4024 3100e7 ld sp,#e700
4027 cd3801 call #0138
402a e6cf and #cf
402c 4f ld c,a
402d e60c and #0c
402f 87 add a,a
4030 87 add a,a
4031 b1 or c
4032 cd3b01 call #013b
4035 cdad59 call #59ad
4038 3ec3 ld a,#c3
403a 214c40 ld hl,#404c
403d 329afd ld (#fd9a),a
4040 229bfd ld (#fd9b),hl
4043 fb ei

#4044:
4044 0101e2 ld bc,#e201
4047 cd4700 call #0047

J'ai écrit un parseur qui permet d'exécuter pas à pas chaque instruction Z80 en Rebol.

De là, comme j'avancais bien, je me suis dit, pourquoi ne pas tester ça en grandeur nature sur un vrai programme ?
J'ai donc récupéré la ROM d'un jeu pas trop gros GALAGA (32ko)
et je me suis mis en tête d'émuler la puce graphique du MSX pour voir si j'arrivais à produire un truc acceptable( pas trop lent donc)

Et j'ai donc abouti à ça:

http://momupload.com/files/13793/galaga.r.html

[url=http://momupload.com/files/13793/galaga.r.html]galaga.r[/url]

(désolé pour le lien, je le mettrais sur mon site ftp, plus tard)

Laissez tourner la demo, jusqu'au bout, vous verrez votre vaisseau se faire capturer, pour ceux qui se souviennent de ce jeu.


Pour l'instant, je ne gère pas encore le clavier donc on peut pas jouer mais ça vaut quand même le coup d'oeil.
Et pis apparement , j'ai quelque embêtements avec les dessins des vaisseaux ennemis, mon interpreteur n'est donc pas encore au point.

Alors , je vois déjà des mains se lever.
- A quand le son ?
Surement jamais, vu qu'émuler une puce sonore en Rebol, même rudimentaire, c'est une autre paire de manche et pis j'y connais rien.
- A quand un Emulateur 100% Rebol permettant de jouer à n'importe que jeu MSX1.
Là c'est faisable mais c'est beaucoup de boulot et ce n'était pas vraiment mon projet de départ.
De plus on remarque quand même que c'est super LENT. Même si mon code n'est pas optimisé, on voit bien que le moteur grapique de Rebol
rame un max quand on zoom, alors que franchement, y'a pas de raison (en tout cas la vitesse de mon code n'est pas en jeu).
J'affiche juste une image 256x192 plus 32 sprites (souvent moins) de 16x16.
guest230-Jan-2007/20:14:45+1:00
Juste pour dire que le programme boucle, même quand vous fermez la fenêtre.
Donc faut penser à killer la tâche.
guest230-Jan-2007/20:30:12+1:00
Juste pour dire que le programme boucle, même quand vous fermez la fenêtre.
Donc faut penser à killer la tâche.
GreG31-Jan-2007/3:00:10+1:00
Je n'arrive pas a chopper ton fichier

Pourtant, j'aurais bien teste! --> FTP !
guest231-Jan-2007/4:06:36+1:00
et vala !
http://perso.orange.fr/rebol/galaga.r

y'a qu'à demander !!!!

En plus c'est une version jouable, jusqu'à ce que ça plante ! hin hin !

Attention !
Pour démarrer le jeu, il faut appuyer sur "Demo", pas sur la touche SPACE, sinon ça bug.

Ensuite les touches pour jouer sont:
espace= pour tirer
flèches droite et gauche pour vous savez quoi.

Vous remarquerez que votre vaisseau est touijours en déplacement sauf quand vous tirez, ça c'est à cause de Rebol qui ne sait pas gérer les touches enfoncées/pas enfoncées. Enfin, j'me comprends.

Le jeu fonctionne jusqu'à ce qu'il rencontre une adresse inconnue. Elle est inconnue parce qu'il ne l'a encore jamais rencontrée. Et comme il vous manque mon parseur pour traduire les nouvelles routines assembleur à la volée, ben du coup, le jeu s'arrête.

Et Oh ! , je vais pas tout vous filer le premier jour
guest231-Jan-2007/4:41:32+1:00
Hum, en même temps, je me dis que si rebcode était diponible on pourrait faire un émulateur vraiment rapide donc exploitable.
Mais reste le problème du rendu, Actuellement Draw est incapable de raffraichir à une cadence de 50Mhz tout en laissant le temps d'exécuter le code du programme plus un wait d'une durée raionnobale pour intercepter les evennements claviers.

Actuellement je ne rafraichi l'écran qu'une fois sur 3 et ça reste lent

... ... ... ... ...
dubman31-Jan-2007/10:25:42+1:00
Guest2 ton idée est trés bonne et originale , c'est pas mal du tout , il est incroyable ce rebol !
Goldevil31-Jan-2007/13:13:26+1:00
Très sympa. Haaa... Galaga (ou Galaxian). Un petite larme nostalgique me vient au coin de l'oeil.

Moi j'ai commencé l'informatique avec un MPF-II, un clone d'AppleII. Le processeur est un 6502 (le même que le Commodore 64 si mes souvenirs sont bons)

Je suis impressionné par le boulot. Je suis surtout impressionné par le fait que cela n'est même pas encore plus lent. Même si le Z80 est un vieux processeur 8 bits, écrire un émulateur demande tout de même du travail ainsi qu'une connaissance fine de la machine à émuler.

Tu pourrais peut-être dissocier l'aspect émulation Z80 et l'émulation MSX (le bios en autre). Ainsi, ton code serait réutilisable pour écrire un émulateur ZX-81 ou ZX-Spectrum.


Félicitation ! C'est une petite perle à publier sur rebol.org


Je n'avais jamais pensé à utiliser rebol pour faire de l'émulation, mais c'est vrai que c'est faisable même pour émuler des processeurs plus modernes. Je pense au 8086 ou encore au 68000. A mon avis, la gars qui fait un émulateur Amiga500 en Rebol recevra un SDK gratuit de la part de Carl même s'il tourne 20x moins vite que la machine originale.
Bonne chance pour émuler les processeurs graphiques



PS: Je suppose que tu parlais d'un rafraîchissement à 50Hz et pas 50MHz.
coccinelle31-Jan-2007/13:45:52+1:00
Bravo, c'est assez génial tout ça.

Et si tu émullais parfaitement le Z80, je pourrais faire tourner mon tout premier programme, un traitement de texte écrit en basic (mBasic sous CPM)
coccinelle31-Jan-2007/14:03:45+1:00
Bravo, c'est assez génial tout ça.

Et si tu émullais parfaitement le Z80, je pourrais faire tourner mon tout premier programme, un traitement de texte écrit en basic (mBasic sous CPM)
guest231-Jan-2007/14:52:25+1:00
Merci pour vos encouragements.
Effectivement, il est possible d'émuler à peu près toutes les machines à base de processeur Z80.
Même si ce projet évolue bien, je continuerais de me co centrer sur la partie émulation du hardware MSX, donc pour les autres machines, faudra trouver des volontaires.

J'ai re-re-reflechi au son, c'est vraiment dommage de pas l'avoir et finalement je pense que ça ne ralentirait pas tant que ça le jeu.

J'ai fait une petite recherche dans Rebol.org et j'ai trouvé le script "Sintezar PM-101 - Phase Manipulation Digital Synthesizer" de Boleslav Brezovsky.
Je pense que ça serait une bonne base de travail, malheureusement je suis un peu pommé par rapport aux notions utilisées et il me semble que le PSG du MSX est beaucoup plus simple.

Les spés de la puce sonnore (PSG) du MSX sont là:
http://www.xs4all.nl/~ronholst/map/resources/sound/generalinstrument_ay-3-8910.pdf
Si une bonne âme voulait se donner la peine d'adapter le script de Boleslav.

Je tiens à préciser que le PSG du MSX et la même que l'amstrad. Donc, le boulot serait réutilisable.

//Goldevil, le bios du MSX (ou de n'importe quel autre machine Z80) est aussi écrit en assembleur donc on pourrait tout simplement l'émuler au lieu de réécrire comme je l'ai fait des API en Rebol. Mais l'émulation serait beaucoup plus lente.
Goldevil1-Feb-2007/9:38:58+1:00
Si mes souvenirs sont bons, le programme de Boleslav crée à la volée des fichiers WAV qui sont joués par rebol. Si tu veux reprendre le même système, tu va devoir en temps réel créer un forme d'onde et générer le fichier wav et puis seulement faire jouer le son.

D'après la datasheet du PSG, il possède 3 canaux, c'est à dire 3 sons de base.

Les sons sont générés un peu comme sur une chaîne de montage

-D'abord un Tone Generator qui te sort une onde carrée à une fréquence bien précise (définie sur 12bits).

-On y mélange éventuellement (avec le MIXER) du "bruit" : une onde carrée pseudo-aléatoire dont la fréquence est définie sur 5 bits.

-On définit l'amplitude avec le Amplitude Controler (infos sur 4 bits).

- Ce dernier peut-être lui-même contrôlé par un Enveloppe Generator. Ce dernier permet de faire varier l'amplitude de manière programmée (Fig 1 schéma avec les lignes en dents de scie)

-En fin de compte le canal nous produit un son sur 4 bits qui passe dans le convertisseur analogique digital pour devenir un son analogique. ce dernier à une réponse logarithmique (voir Fig 3 de la doc).


Attention, la manière dont est conçu le circuits audio influence aussi le son dans l'étage analogique lui-même. Souvent, il y a un amplificateur opérationnel et quelques composants (condensateur, diodes et résistance) qui monte le voltage pour le faire correspondre à une sortie casque ou Line par exemple. Mais ce traitement modifie également un peu le son. Il est souvent plus "lissé".

Autre chose. Si Rebol est incapable de jouer plusieurs sons simultanément, il faudra en plus mixer les 3 canaux en un seul.


Comment on programme le PSG ? Il y a trois modèles mentionnés dans la doc avec 0,1 ou 2 ports de communication I/O. Mais les 3 modèles peuvent être aussi interfacés avec un bus et dans ce cas, le processeur de l'ordi voit les registres du PSG comme de simples emplacement mémoire.

Il faudrait déjà savoir quel PSG est utilisé et comment il est interfacé avec le CPU.


Le gros problème avec l'émulation du son, c'est qu'il faut émuler un autre type de processeur en même temps. l'avantage du PSG est qu'il est capable de jouer un son en continu. Le CPU change les registres et le PSG joue le son tant que le CPU ne lui demande pas de s'arrêter.

Générer les formes d'ondes, c'est pas compliqué. C'est du calcul sur des entiers et c'est assez rapide. Par contre, j'ai peur du temps de traitement pour générer le wav et demander à rebol de le jouer.


Il y a plusieurs pistes pour améliorer le système :
1)Quand un son est généré, il a un signature bien précise qui est les valeurs des 16 regitres du PSG. On peut le conserver en mémoire et/ou sur disque.
Lors du premier "boum" de votre vaisseaux dans Galaga, le jeu ralenti mais à la perte de vos deux autres vaisseaux la vitesse reste optimale.

2) On génère à l'avance plusieurs types de sons que le PSG peut créer. On prépare des wav à l'avance quoi. Je suis certains que la plupart des softs utilisent qu'un nombre limités de sons courant. Par exemple, les musiques sont souvent faites



Voilà, j'espère t'avoir un peu aidé.
guest21-Feb-2007/14:49:04+1:00
Salut Goldevil, merci pour tes infos.
En fait, dans le MSX, on dispose de ports pour modifier directement les registres du PSG.
Dans le jeu Galaga, c'est encore plus simple, il appelle une routine du bios en lui passant en parametre le numero du registre à modifier et sa nouvelle valeur.
J'ai déjà tracé la modifications des registres et j'obtiens une série de 19 registres à modifier à chaque interruption (c'est à dire 50 fois par seconde).

par exemple, la musique jouée au lancement du jeu commence par cette série:
(seuls les registres R0 à R15 nous intéressent)

R0 R1 R2 R3 R4 R5 R7 R10R11R12R13R14R15R16R17R18
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 9A 0A 31 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 31 0A 5C 0D 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 31 0A 5C 0D 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 31 0A 5C 0D 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 31 0A 5C 0D 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 31 0A 5C 0D 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 31 0A 31 0A 5C 0D 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 14 09 E7 0B E7 0B 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 5C 0D 14 09 02 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 5C 0D 14 09 02 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 5C 0D 14 09 02 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 5C 0D 14 09 02 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 5C 0D 14 09 02 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 5C 0D 14 09 02 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF
PSG: 16 08 5C 0D 9A 0A 00 B8 0D 0D 0F 00 00 00 00 CF

Dans un premier temps ont pourrait essayer de ne prendre en compte que les 6 premiers registres (la fréquence de la note pour les 3 canaux, si je ne me trompre pas) et y appliquer dessus une simple onde sinusoidale et mixer les 3 canaux, pour voir si ça sort un son)
En fait si le son final est différent du son original, c'est pas bien grave, du moment que ça sort quelque chose

Mais je garde ton idée de sauvegarder dans une table en temps réel , les données des registres du PSG et d'y associer là séquence de son correspondante.
Mais j'aimerais quand même être capable de générer le son à la volée plutot que de faire appel à des fichiers WAV samplés par un autre programme.


//sinon pour le prog de Boslav, d'après ce que j'ai vu, il'y a 2 modes de fonctionnement:
Création d'un fichier wav ou envoie directe des infos au format PCM sur le port sound://
C'est evidemment le 2nd cas qui m'intéresse.
guest21-Feb-2007/15:09+1:00
Autres infos:
Sur ce site vous trouver tout un tas de programmes permettant d'émuler le PSG du MSX:
http://bulba.at.kz/progr_e.htm

Dont celui là:
http://bulba.at.kz/YMEmuWithFilters.src.rar
qui est le source d'un convertisseur AY->WAV écrit en pascal.

Des volontaires ????
guest21-Feb-2007/17:52:05+1:00
Bon finalement j'ai fait des tests avec le port sound:// et je crois avoir quelques résultats:

    REBOl []
    

    ; Set up the sound sample parameters:
    sample: make sound [
        rate: 8000 
        channels: 1
        bits: 8
        volume: 1
        data: #{}
    ]

    
    ; build a wave at a frequency during a time (time from 1 to 50, 50 = 1 sec) 
    make-tone: func [freq time] [
    	pitch: freq  * 360 / sample/rate
    	note: make binary! to integer! 360 / pitch
	for phase 1 360 pitch [
		val: 128 + to integer! 127 * sine phase
		insert tail note to-char val
	]
	rep: freq / 50 * time
        ret: make binary! rep * length? note
        head insert/dup tail ret note rep
        
    ]

    ; fréquence 200 Hz pendant 1/2 secondes
    note1: make-tone 200 25
 
	wait 0 ; nécessaire pour que le port sound fonctionne
	sound-port: open sound://
	sample/data: note1
	insert sound-port sample
	probe now/time/precise
	wait sound-port
	probe now/time/precise
        close sound-port
    halt



Mais comment interpréter ce qui suit ?
J'ai bien compris que les registres ne contiennent pas directement la fréquence à appliquer mais je me perds dans l'interprétation du texte (mon anglais est vraiment mauvais)

*******
Tone Generator Control (registers R0,R1,R2,R3,R4,R5)
   The frequency of each square wave generated by the three Tone Generators
   (one each for Channels A, B and C) is obtained in the PSG by first
   counting down the input clock by 16, then by further counting down
   the result by the programmed 12-bit Tone Period value.
*********
Franck1-Feb-2007/23:55:50+1:00
Vraiment excellent super boulot, niveau vitesse c'est tout à fait acceptable (à mon goût)
Goldevil2-Feb-2007/15:37:35+1:00
Si j'ai bien compris tout est géré sur 4 bits. Chaque canal est un son sur 4 bits dont l'amplitude varie entre -7 et 7 (4 bits signés). Dans le PSG, il y a deux blocs séparés :
1) Le génération de la note qui peut-être un mélange d'un son pur et d'un "bruit".
2) Le contrôle du volume qui peut varier au cours du temps.



1) TONE GENERATOR
Le paramètre freq de la fonction 'make-tone se calcule à partir de registres différents pour chaque canal.
Canal A : 4 bits de poids faible de R1 + les 8 bits de R0
Canal B : 4 bits de poids faible de R3 + les 8 bits de R2
Canal C : 4 bits de poids faible de R5 + les 8 bits de R4

La période (l'inverse de la fréquence) est donc sur 12 bits (0 à 4093). De plus, le PSG utilise la fréquence de base comme étant 1/16ème de la fréquence d'horloge.

D'après la doc le PSG est peut être cadencé entre 1MHz et 2MHz. Le modèle AY-3-8913 peut monter à 2,5 MHZ.
(J'ai bien écrit MHz et pas GHz ! C'est une autre époque les gars)

Calcul de la fréquence à partir de la valeur du registre :

horloge_du_psg / 16 / valeur_registre

Autre détail, le tone generator produit une onde carrée et non sinusoidale. Donc la boucle "for phase 1 360 pitch..." doit faire son calcul différement.

J'ai fait une propose donc une réécriture du code ou la fonction 'make-tone prend comme paramètre la valeur du registre et produit une onde carrée au lieu d'une sinusoide. A l'audition le son est plus coupant. Il y a des aigus car une onde carrée implique des autre fréquences.
Dans l'exemple donné, on génère un son digital avec une fréquence d'échantillonage de 8Khz (sample/rate). Je l'ai changé en 44.1Khz, qualité CD.

Chose amusante, le PSG peut sortir des fréquences supérieure à 50Khz. Soit de bon ultrasons qui ne sont même plus audibles par un chien.

2) NOISE GENERATOR

Alors là je sèche un peu. Le noise generator fonctionne sur le même principe que le tone generator à l'exception qu'il génère un bruit pseudo aléatoire. Il prend sa fréquence sur 5 bits du registre R6. Le seul problème étant de savoir ce qu'on entend par "bruit pseudo-aléatoire" d'une fréquence particulière.

3) MIXER

Le principe est très simple. Les bits du registre R7 jouent comme des interrupteurs. Pour les trois canaux, on choisi le son, le bruit ou les deux. Par contre je ne sais pas comment il mélange son + bruit mais je suppose qu'un s'agit d'une simple addition des deux.

4) AMPLITUDE CONTROL

Il s'agit du contrôle du volume. Il prend sa valeur sur 4 bits faibles d'un registre (valeurs de 0 à 15).
Canal A : Registre R10
Canal B : Registre R11
Canal C : Registre R12

Le truc bizare c'est le 5ème bit le plus significatifs du registre qui défini le "Amplitude Mode". Je pense que cela détermine 2 modes de fonctionnement possible. 0 = fonctionnement de base. Sa valeur est commandée par le "Envelope Generator". Je suppose que les 4 bits sont alors ignorés.

5) ENVELOPPE GENERATOR
L'enveloppe est un contrôle de l'amplitude programmée. Selon 4 bits faibles du registre R15, il va produire une enveloppe d'une forme différente. Dans la doc, la Figure 1 montre les différentes combinaisons possibles. Imaginez simplement que vous jouer avec le curseur volume de la chaîne hifi.
L'amplitude de ce contrôle de volume est donnée sur 4 bits qui servent à controler le "Amplitude Control"

En résumé. Soit on garde le volume au même niveau, soit on demande au Enveloppe Generator de le changer selon une schéma précis. Notez qu'il semble que le Enveloppe Generator contrôle le volume des 3 canaux ensemble. On ne peut donc pas appliquer des enveloppes différentes selon le canal.


6) D/A CONVERTER

Pour chaque canal, ce module agit comme un simple multiplicateur sur le signal. Il multiplie le signal sorti du Mixer par le signal sorti du "Amplitude Genrator".

Il s'agit de la dernière étape qui transform des bits en un signal qui va de 0 à 1V. On pourrait ce dire qu'on s'en fout car nous on veut obtenir un signal digital qu'on a déjà à ce stade. Mais il a une sortie logarithmique. Il faut donc effectuer en plus un exposant sur le signal pour(cfr fig 3)



Bon, c'est bien joli tout ça mais comment faire cela.

En fait je pense qu'il faut faire une fonction make-tone qui génère en une seule boucle le signal pour une durée de quelques seconde. On joue le son et lorsque l'émulateur rencontre une modification du registre, il recrée un nouveau signal et lance le son à la place de celui qui est en train d'être joué.

Je pense qu'il n'est pas envisageable de créer le son par plusieurs étapes successives (trop lent)


REBOl []

	; Set up the sound sample parameters:
	sample: make sound [
		rate: 44100 
		channels: 1
		bits: 8
		volume: 1
		data: #{}
	]

	psg_clock: 1000000 
	
	; build a wave at a frequency during a time (time from 1 to 50, 50 = 1 sec) 
	make-tone: func [
		tune_register [integer!]
		time [integer!]
		/local 
			freq
	] [
		freq: psg_clock / 16 / tune_register 
		pitch: freq  * 360 / sample/rate
		note: make binary! to integer! 360 / pitch
		for phase 1 360 pitch [
			val: 128 + to integer! 127 * sine phase
			insert tail note to-char val
		]
		rep: freq / 50 * time
		ret: make binary! rep * length? note
		head insert/dup tail ret note rep
	]

	; On teste avec la valeur du registre à 50 pendant 1/2 seconde
	note1: make-tone 50 25
 
	wait 0 ; nécessaire pour que le port sound fonctionne
	sound-port: open sound://
	sample/data: note1
	insert sound-port sample
	probe now/time/precise
	wait sound-port
	probe now/time/precise
	close sound-port
guest22-Feb-2007/17:56:19+1:00
Merci Goldevil
j'ai enfin trouvé la fréquence du PSG:
Sur le MSX, c'est la fréquence du Z80 divisé par 2, soit:

psg_clock: 3579545 / 2

et j'ai trouvé une description un peu plus accessible des registres du PSG
The AY-3-8910 is a I/O chip whith 3 sound generators.
It controls the three MSX std. audio channels, joystick and cassette.

Function/register table:
Frequency, audio channel A-C:   0...5
Noise freq.:                    6
Mixer:                          7
Volume:                         8 ...10
Envelope:                       11...13
Joystick and cassette:          14
Paddle,joystick sel,touchpad:   15

For register summary, see note 6a1 (port A0).
For SOUND A,B (register write) example, see note 6b1 (port A1).
For register read example, see note 6c1 (port A2).
For joystick read example, see note 6c1 (port A2).

Note 6a1:
PORT.A0         I AY-3-8910 PSG      Register select
-------

Port A0h WRITE = PSG register select.
This port selects the current PSG register (0-15).

Registers are:
0 = Fine freq. channel A        (0-255)
1 = Freq. channel A             (0-15)
2 = Fine freq. channel B        (0-255)
3 = Freq. channel B             (0-15)
4 = Fine freq. channel C        (0-255)
5 = Freq. channel C             (0-15)

        Output frequency (tone):

        f =          3.579M/2
         T      -------------------
                16*(256*fine+coarse)

        Where "coarse" are one of the coarse frequency setting registers, 1,3 or 5
        and "fine" are one of the fine frequency setting registers, 1,2 or 4.

6 = Noise period                (0-31)

        Output frequency (noise):

        f =        3.579M/2
         N      --------------
                16*NoisePeriod

        Where "NoisePeriod" are register 6.

7 = Mixer
        bit   Expl.
        0 = Channel A tone enable       (0=Enable,1=Disable)
        1 = Channel B tone enable       (0=Enable,1=Disable)
        2 = Channel C tone enable       (0=Enable,1=Disable)
        3 = Channel A noise enable      (0=Enable,1=Disable)
        4 = Channel B noise enable      (0=Enable,1=Disable)
        5 = Channel C noise enable      (0=Enable,1=Disable)
        6 = I/O port A mode             (0=input, 1=Output)
        7 = I/O port B mode             (0=input, 1=Output)
8 = Volume channel A            (0-15, 16=Envelope)
9 = Volume channel B            (0-15, 16=Envelope)
10= Volume channel C            (0-15, 16=Envelope)
11= Envelope fine freq.         (0-255)
12= Envelope freq.              (0-255)

        Envelope frequency (tone or noise):


        f =            3.579M/2
         E      ----------------------
                256*(256*ECoarse+EFine)

        Where "ECoarse" are the coarse envelope frequency setting register 12
        and "EFine" are the fine envelope frequency setting register 11.
        Note that the envelope period is  

                 1
               -----
                f
                 E

13= Envelope shape              (0-15)

        C A A H
        O T L L
        N T T D
        T
        0 0 X X  \________   0
        0 1 X X  /________   4
        1 0 0 0  \\\\\\\\\   8  (Repeating, see figure)
        1 0 0 1  \________   9
        1 0 1 0  \/\/\/\/\   10 (Repeating, see figure)
        1 0 1 1  \           11 (See figure)
        1 1 0 0  /////////   12 (Repeating)
        1 1 0 1  /           13
        1 1 1 0  /\/\/\/\/\  14 (Repeating)
        1 1 1 1  /           15


Sinon, tu parlais de générer une onde carrée, c'est pas l'urgence à mon avis.
Je vais plutôt essayer d'adapter le script pour gérer le mixer, l'amplitude et l'enveloppe, ça me parait faisable assez vite, le plus dûr est passé...
Je ferai ça ce week, sauf si tu veux t'en charger
guest22-Feb-2007/18:02:45+1:00
ah au fait !
J'ai déjà testé l'idée de faire durer le son entre 2 interruptions, puis de clearer le port et de réinjecter le son suivant.
Ben... ça marche pas trop bien.
déjà l'émulateur est très lent donc la musique original ne ressemblera plus à rien et puis j'entends de désagréables crachottis lorsque je clear le port sound.
Donc va falloir repartir sur l'idée de pré-construire tous les sons en utilisant le générateur AY sur lequel on bosse ou alor d'envoyer les sons avec un décalage de 1 à plusieurs secondes, pour laisser le temps à l'émulateur de fabriquer la séquence sonore complète.

je me tate...
guest22-Feb-2007/18:12:03+1:00
Arghhhhh !!!
j'étais complètement passé à côté du script KurtSynth de Kurt Sassenrath.
http://www.rebol.net/demos/1B5E455C332A1F7C/kurtsynth.r
Si je l'avais trouvé avant j'aurais moins galéré.
Son code n'est pas très optimisé mais ça marche.

Bon sinon, une ptite question Goldevil...
Pour le mixage des 3 canaux a b c, je fais une bête moyenne ou pas ?
F = Fa/3 + Fb/3 + Fc/3
guest22-Feb-2007/18:14:25+1:00
Euh evidemment, je divise pas par 3 si il n'y que 1 ou 2 canaux en fonction.
(je précise)
guest23-Feb-2007/18:59:21+1:00
Bon vala, j'ai fait un script pour jouer la musique d'intro de Galaga:

la chanson contient un liste de 15 valeurs correspondant aux registres du PSG, rafraichis tous les 1/50 de seconde.

J'ai uniquement tenté de mixer les fréquences des 3 premiers canaux, sans toucher au reste (amplitude, enveloppe et noise).

D'ailleurs on devrait obtenir un résultat assez proche de l'original, vue que dans cette musique, le volume des 3 canaux est pratiquement le même et que la musique n'utilise pas d'envelopes ni le canal noise.

On reconnait bien le tempo mais malheureusement les notes jouées ne correspondent pas du tout.
Apparement ça ne vient pas du Mixer, j'ai fait des tests, le problème c'est la fréquence des notes contenues dans les canaux A,B et C, ça ne correspond pas du tout aux fréquences réelles attendues par le format PCM (celui du port sound://). Y'a probablement une conversion suplémentaire à faire.

ici, j'ai mis le wav généré par l'émulateur BlueMSX, c'est donc le but à atteindre:
http://perso.orange.fr/rebol/GALAGA_01.wav


REBOL []
   
   clock: 3579545 / 32  
   inter: 50 ; nombre d'interruptions par seconde
   wait 0 ; nécessaire pour que le port sound fonctionne
   sound-port: open sound://


    ; Set up the sound sample parameters:
    sample: make sound [
        rate: 44100 / 4
        channels: 1
        bits: 8
        volume: 1
        data: #{}
    ]

   cache: make list! 100
    ; build a wave at a frequency and store it for reuse
   make-tone: func [freq /local note rep ret pitch phase] [
    	if not note: select head cache freq [
		pitch: freq  * 360 / sample/rate
		note: make binary! to integer! 360 / pitch 
		phase: 0
		while [phase <= 360][
			val: 128 + to integer! 127 * sine phase
			insert tail note to char! val
			phase: phase + pitch
		]
	insert cache freq
	insert cache note
	]
        cp note
        
    ]
    
    ; Mix 3 channels A,B,C
    mix: func [/local tone len][
    	tone: make binary! len: to-integer sample/rate / inter
    	tone: head change/dup tone to char! 0 len
    	loop len [
    	    	if tail? toneA [toneA: head toneA]
	    	if tail? toneB [toneB: head toneB]
    		if tail? toneC [toneC: head toneC]
      		val: to integer! toneA/1 + toneB/1 + toneC/1 / 3
    		change tone to char! val
    		tone: next tone
    		toneA: next toneA
    		toneB: next toneB
    		toneC: next ToneC
    	]
    	head tone
    ]

; une ligne correspond au son joué pendant 1/50 de seconde
song: [
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 154 10 49 10 0 184 13 13 15 0 0 0 0 207
49 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
49 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
49 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
49 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
49 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
49 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
20 9 231 11 231 11 0 184 13 13 15 0 0 0 0 207
92 13 20 9 2 10 0 184 13 13 15 0 0 0 0 207
92 13 20 9 2 10 0 184 13 13 15 0 0 0 0 207
92 13 20 9 2 10 0 184 13 13 15 0 0 0 0 207
92 13 20 9 2 10 0 184 13 13 15 0 0 0 0 207
92 13 20 9 2 10 0 184 13 13 15 0 0 0 0 207
92 13 20 9 2 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 92 13 154 10 0 184 13 13 15 0 0 0 0 207
22 8 49 10 92 13 0 184 13 13 15 0 0 0 0 207
22 8 49 10 92 13 0 184 13 13 15 0 0 0 0 207
22 8 49 10 92 13 0 184 13 13 15 0 0 0 0 207
22 8 49 10 92 13 0 184 13 13 15 0 0 0 0 207
22 8 49 10 92 13 0 184 13 13 15 0 0 0 0 207
22 8 49 10 92 13 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
49 10 22 8 241 7 0 184 13 13 15 0 0 0 0 207
49 10 22 8 241 7 0 184 13 13 15 0 0 0 0 207
49 10 22 8 241 7 0 184 13 13 15 0 0 0 0 207
49 10 22 8 241 7 0 184 13 13 15 0 0 0 0 207
49 10 22 8 241 7 0 184 13 13 15 0 0 0 0 207
49 10 22 8 241 7 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 154 10 234 8 0 184 13 13 15 0 0 0 0 207
92 13 49 10 92 13 0 184 13 13 15 0 0 0 0 207
92 13 49 10 92 13 0 184 13 13 15 0 0 0 0 207
92 13 49 10 92 13 0 184 13 13 15 0 0 0 0 207
92 13 49 10 92 13 0 184 13 13 15 0 0 0 0 207
92 13 49 10 92 13 0 184 13 13 15 0 0 0 0 207
92 13 49 10 92 13 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
231 11 231 11 231 11 0 184 13 13 15 0 0 0 0 207
2 10 20 9 2 10 0 184 13 13 15 0 0 0 0 207
2 10 20 9 2 10 0 184 13 13 15 0 0 0 0 207
2 10 20 9 2 10 0 184 13 13 15 0 0 0 0 207
2 10 20 9 2 10 0 184 13 13 15 0 0 0 0 207
2 10 20 9 2 10 0 184 13 13 15 0 0 0 0 207
2 10 20 9 2 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 92 13 154 10 0 184 13 13 15 0 0 0 0 207
154 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
154 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
154 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
154 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
154 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
154 10 49 10 92 13 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 231 11 234 8 0 184 13 13 15 0 0 0 0 207
231 11 234 8 19 7 0 184 13 13 15 0 0 0 0 207
231 11 234 8 19 7 0 184 13 13 15 0 0 0 0 207
231 11 234 8 19 7 0 184 13 13 15 0 0 0 0 207
231 11 234 8 19 7 0 184 13 13 15 0 0 0 0 207
231 11 234 8 19 7 0 184 13 13 15 0 0 0 0 207
231 11 234 8 19 7 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 106 8 224 11 0 184 13 13 15 0 0 0 0 207
60 11 234 8 127 7 0 184 13 13 15 0 0 0 0 207
60 11 234 8 127 7 0 184 13 13 15 0 0 0 0 207
60 11 234 8 127 7 0 184 13 13 15 0 0 0 0 207
60 11 234 8 127 7 0 184 13 13 15 0 0 0 0 207
60 11 234 8 127 7 0 184 13 13 15 0 0 0 0 207
60 11 234 8 127 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 106 8 0 184 13 13 15 0 0 0 0 207
92 13 60 11 234 8 0 184 13 13 15 0 0 0 0 207
92 13 60 11 234 8 0 184 13 13 15 0 0 0 0 207
92 13 60 11 234 8 0 184 13 13 15 0 0 0 0 207
92 13 60 11 234 8 0 184 13 13 15 0 0 0 0 207
92 13 60 11 234 8 0 184 13 13 15 0 0 0 0 207
92 13 60 11 234 8 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 231 11 2 10 0 184 13 13 15 0 0 0 0 207
145 8 92 13 60 11 0 184 13 13 15 0 0 0 0 207
145 8 92 13 60 11 0 184 13 13 15 0 0 0 0 207
145 8 92 13 60 11 0 184 13 13 15 0 0 0 0 207
145 8 92 13 60 11 0 184 13 13 15 0 0 0 0 207
145 8 92 13 60 11 0 184 13 13 15 0 0 0 0 207
145 8 92 13 60 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 145 8 231 11 0 184 13 13 15 0 0 0 0 207
60 11 231 11 145 8 0 184 13 13 15 0 0 0 0 207
60 11 231 11 145 8 0 184 13 13 15 0 0 0 0 207
60 11 231 11 145 8 0 184 13 13 15 0 0 0 0 207
60 11 231 11 145 8 0 184 13 13 15 0 0 0 0 207
60 11 231 11 145 8 0 184 13 13 15 0 0 0 0 207
60 11 231 11 145 8 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 60 11 127 7 0 184 13 13 15 0 0 0 0 207
127 7 2 10 224 11 0 184 13 13 15 0 0 0 0 207
127 7 2 10 224 11 0 184 13 13 15 0 0 0 0 207
127 7 2 10 224 11 0 184 13 13 15 0 0 0 0 207
127 7 2 10 224 11 0 184 13 13 15 0 0 0 0 207
127 7 2 10 224 11 0 184 13 13 15 0 0 0 0 207
127 7 2 10 224 11 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
234 8 60 11 127 7 0 184 13 13 15 0 0 0 0 207
60 11 92 13 234 8 0 184 13 13 15 0 0 0 0 207
60 11 92 13 234 8 0 184 13 13 15 0 0 0 0 207
60 11 92 13 234 8 0 184 13 13 15 0 0 0 0 207
60 11 92 13 234 8 0 184 13 13 15 0 0 0 0 207
60 11 92 13 234 8 0 184 13 13 15 0 0 0 0 207
60 11 92 13 234 8 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 2 10 241 7 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 231 11 2 10 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
231 11 20 9 231 11 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 231 11 234 8 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 22 8 154 10 0 184 13 13 15 0 0 0 0 207
234 8 20 9 231 11 0 184 13 13 15 0 0 0 0 207
234 8 20 9 231 11 0 184 13 13 15 0 0 0 0 207
154 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
154 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
154 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
154 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
154 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
154 10 20 9 231 11 0 184 13 13 15 0 0 0 0 207
]
pcm: clear #{}
Fa': Fb': Fc': 0
foreach [A0 A1 B0 B1 C0 C1 noise mixer volA volB volC Ev0 Ev1 Shape JoyA JoyB] song [
	Fa: to integer! 0.5 + clock / (A0 and 15 * 256 + A1)
	Fb: to integer! 0.5 + clock / (B0 and 15 * 256 + B1)
	Fc: to integer! 0.5 + clock / (C0 and 15 * 256 + C1)
	;print [Fa Fb Fc]
	if Fa' <> Fa [toneA: make-tone Fa]
	if Fb' <> Fb [toneB: make-tone Fb]
	if Fc' <> Fc [toneC: make-tone Fc]
	insert tail pcm mix
	Fa': Fa Fb': Fb Fc': Fc
]
sample/data: pcm
insert sound-port sample

halt
Goldevil3-Feb-2007/19:23:16+1:00
Pour l'onde carrée. C'est plus simple que l'onde sinusoïdale car tu ne dois pas faire de calcul de sinus. La moitié de la période, la valeur est au maximum (+7) et l'autre moitié au minimum (-7). Et puis le son ressemblera plus à ce que fait le PSG.

Pour mixer les 3 canaux, tu additionne les 3 canaux (en vérifiant de ne pas sortir des limites). Je pense que tu peux créer des sons sur 2 canaux sans devoir mixer toi-même. Dans ton 1er script, dans les paramètres du son, tu détermine le nombre de canaux.
Et je ne sais pas comment doivent être encodées les données dans ce cas.

La doc sur le support du son en Rebol est dispo dans celle du SDK :
http://www.rebol.com/docs/sound.html

J'ai écrit un petit script fonctionnel. C'est comme cela que je vois un générateur de son émulant le PSG. Le code est particulièrement peu optimisé pour des raisons didactiques (chaque registre est un object!) mais c'est pour montrer comment les différent registres influent sur le calcul de l'onde résultante de chaque canal.
Seul le Enveloppe Generator n'est pas du tout émulé

rebol []

psg_emulator: make object! [

	; PARAMETERS
	psg_clock: 3579545 / 2
	psg_period: 1 / psg_clock
	sampling_frequency: 16000
	time_slot_size: to-integer (psg_clock / sampling_frequency)
	
	
	sample_channel_A: make sound [
    	rate: sampling_frequency
    	channels: 1
    	bits: 8
    	volume: 1
    	data: #{}
	]
	sample_channel_B: make sound [
    	rate: sampling_frequency
    	channels: 1
    	bits: 8
    	volume: 1
    	data: #{}
	]
	sample_channel_C: make sound [
    	rate: sampling_frequency
    	channels: 1
    	bits: 8
    	volume: 1
    	data: #{}
	]
	sample_channel_mixed: make sound [
    	rate: sampling_frequency
    	channels: 1
    	bits: 8
    	volume: 1
    	data: #{}
	]
	
	; REGISTERS
	
	register: make object! [
		bits: reduce [true true true true true true true true ] ; B0 B1 B2 B3 B4 B5 B6 B7
		
		init: does [ bits: reduce bits ]

		get_bit: func [
			bit_number [integer!] 
		] [
			return pick bits bit_number
		]	
		
		getvalue: func [
			/limit 	; pour limiter le nombre de bits significatifs
				nb_bits_limit
			/local
				bit
				i
		] [
			value: 0
			either limit [nb_limit: nb_bits_limit] [nb_limit: 7]
			for i 0 nb_limit 1 [ if (pick bits (i + 1)) = TRUE [ value: value + ( 2 ** i )] ]
			return value
		]
		
		B0: does [ return first bits]
		B1: does [ return second bits ]
		B2: does [return third bits]
		B3: does [return fourth bits]
		B4: does [return fifth bits]
		B5: does [return sixth bits]
		B6: does [return seventh bits]
		B7: does [return pick bits 8]
	]
	
	; Tone generator channel A
	R0: make register []
	R1: make register []
	; Tone generator channel B
	R2: make register []
	R3: make register []
	; Tone generator channel C
	R4: make register []
	R5: make register []
	; Noise 
	R6: make register []
	; Mixer control
	R7: make register []
	; Amplitude control channel A
	R10: make register []
	; Amplitude control channel B
	R11: make register []
	; Amplitude control channel C
	R12: make register []
	; Envelope generator frequency
	R13: make register []
	R14: make register []
	; Envelope generator shape
	R15: make register []
	
	; SOUND GENERATION
	
	init: does [
    	R0/init
    	R1/init
    	R2/init
    	R3/init
    	R4/init
    	R5/init
    	R6/init
    	R7/init
    	R10/init
    	R11/init
    	R12/init
    	R13/init
    	R14/init
    	R15/init
		time_slot_size: psg_clock / sampling_frequency
		tone_generator/init
		noise_generator/init
		enveloppe_generator/init
		sample_channel_A: make sound [
        	rate: sampling_frequency
        	channels: 1
        	bits: 8
        	volume: 1
        	data: #{}
    	]
		sample_channel_B: make sound [
        	rate: sampling_frequency
        	channels: 1
        	bits: 8
        	volume: 1
        	data: #{}
    	]
		sample_channel_C: make sound [
        	rate: sampling_frequency
        	channels: 1
        	bits: 8
        	volume: 1
        	data: #{}
    	]
		sample_channel_mixed: make sound [
        	rate: sampling_frequency
        	channels: 1
        	bits: 8
        	volume: 1
        	data: #{}
    	]
	]
	
	generate_sound: func [
		duration [integer!] 
		/local
			nb_cycles
			noise_level
	] [
		time_slot: 0
		duration: to-decimal duration
		nb_cycles: to-integer (duration * psg_clock)
		init
		print ["Duration in seconds:" duration]
		print ["Duration in cycles:" nb_cycles]
		print ["Sound channel A :" r7/B0]
		print ["Sound channel B :" r7/B1]
		print ["Sound channel C :" r7/B2]
		print ["Noise channel A :" r7/B3]
		print ["Noise channel B :" r7/B4]
		print ["Noise channel C :" r7/B5]
		print ["Volume channel A :" r10/getvalue/limit 4]
		print ["Volume channel B :" r11/getvalue/limit 4]
		print ["Volume channel C :" r12/getvalue/limit 4]

		print "Calculating..."
		for time_slot 0.001 duration ( 1 / sampling_frequency ) [

			channel_A: 0 ; Channel A
			channel_B: 0 ; Channel B
			channel_C: 0 ; Channel C
			
			; TONE GENERATOR + NOISE GENERATOR
			
			; noise level
			noise_level: noise_generator/getvalue time_slot
			; sortie du mixer pour channel A
			if r7/B0 [channel_A: channel_A + tone_generator/getvalue time_slot #"A"]
			if r7/B3 [channel_A: channel_A +  noise_level]
			if not all [r7/B0 r7/B3] [channel_A: channel_A * 2]
			
			; sortie du mixer pour channel B
			if r7/B1 [channel_B: channel_B + tone_generator/getvalue time_slot #"B"]
			if r7/B4 [channel_B: channel_B + noise_level]
			if not all [r7/B1 r7/B4] [channel_B: channel_B * 2]
			
			; sortie du mixer pour channel C
			if r7/B2 [channel_C: channel_C + tone_generator/getvalue time_slot #"C"]
			if r7/B5 [channel_C: channel_C + noise_level]
			if not all [r7/B2 r7/B5] [channel_C: channel_C * 2]
			
			;print [channel_A channel_B channel_C noise_generator/current_level]
			
			; AMPLITUDE GENERATOR
			either r10/B4 [ 
				channel_A: channel_A * enveloppe_generator/getvalue #"A"
			] [
				channel_A: channel_A * (r10/getvalue/limit 4) / 15
			]
			either r11/B4 [ 
				channel_B: channel_B * enveloppe_generator/getvalue #"B"
			] [
				channel_B: channel_B * (r11/getvalue/limit 4) / 15
			]
			either r12/B4 [ 
				channel_C: channel_C * enveloppe_generator/getvalue #"C"
			] [
				channel_C: channel_C * (r12/getvalue/limit 4) / 15
			]

			;print [channel_A channel_B channel_C noise_generator/current_level]
			channel_mixed: channel_A + channel_B + channel_C 
			channel_mixed: max channel_mixed -10
			channel_mixed: min channel_mixed 10
									
			; DA CONVERTIONS
			
			; Logarithmic amplification
			channel_A: to-integer (channel_A * abs channel_A) + 128
			channel_B: to-integer (channel_B * abs channel_B) + 128 
			channel_C: to-integer( channel_C * abs channel_C) + 128 
			channel_mixed: to-integer( channel_mixed * abs channel_mixed) + 128 
			
			
			;print [channel_A channel_B channel_C]
			
				
			append sample_channel_A/data to-char channel_A
			append sample_channel_B/data to-char channel_B
			append sample_channel_C/data to-char channel_C
			append sample_channel_mixed/data to-char channel_mixed
			
			; Mix the 3 channels
			output: channel_A + channel_B + channel_C
		]
		return output
	]
	
	tone_generator: make object! [
	
		freq_channel_A: freq_channel_B: freq_channel_C: 0
		period_channel_A: period_channel_B: period_channel_C: 0
		halfperiod_channel_A: halfperiod_channel_B: halfperiod_channel_C: 0
				
		; Initialisation à partir des registres
		init: does [
			freq_channel_A: psg_clock / 16 / (((R1/getvalue/limit 4) * 256) + R0/getvalue )
			freq_channel_B: psg_clock / 16 / (((R3/getvalue/limit 4) * 256) + R2/getvalue ) 
			freq_channel_C: psg_clock / 16 / (((R5/getvalue/limit 4) * 256) + R4/getvalue ) 
			period_channel_A: 1 / freq_channel_A
			period_channel_B: 1 / freq_channel_B
			period_channel_C: 1 / freq_channel_C
			halfperiod_channel_A: period_channel_A / 2
			halfperiod_channel_B: period_channel_B / 2
			halfperiod_channel_C: period_channel_C / 2
			print "TONE GENERATOR"
			print ["Frequency channel A : " freq_channel_A "Hz (Period: " (period_channel_A * 1000) "ms)"]
			print ["Frequency channel B : " freq_channel_B "Hz (Period: " (period_channel_B * 1000) "ms)"]
			print ["Frequency channel C : " freq_channel_C "Hz (Period: " (period_channel_C * 1000) "ms)"]
		]
	
		; Donne valeur signal pour un moment donné
		getvalue: func [
			time_slot
			channel [char!] 
			/local
				time_position
		] [
			switch channel [
				#"A" [ either  (modulo time_slot period_channel_A) > halfperiod_channel_A [ return -5] [ return 5] ]
				#"B" [ either  (modulo time_slot period_channel_B) > halfperiod_channel_B [ return -5] [ return 5] ]
				#"C" [ either  (modulo time_slot period_channel_C) > halfperiod_channel_C [ return -5] [ return 5] ]
			]
			return value
		]
	]
	
	noise_generator: make object! [
	
		freq_channel_noise: 0
		period_channel_noise: 0
		current_level: 0
		last_slot: -1
		
		init: does [
			freq_channel_noise: psg_clock / 16 /  R6/getvalue/limit 5
			period_channel_noise: 1 / freq_channel_noise
			last_slot: -1
			print ["Frequency noise channel : " freq_channel_noise "Hz (Period: " (period_channel_noise * 1000) "ms)"]
		]
	
		getvalue: func [
			timeslot
		] [
			slot: to-integer timeslot / period_channel_noise
			;print slot
			if slot <> last_slot [
				either (random 2) = 1 [current_level: -5] [current_level: 5]
				;print ["change to" current_level]
				last_slot: slot
			] 
			return current_level
		]
		
	]
	
	enveloppe_generator: make object! [
		init: does []
		
		getvalue: func [
			channel [char!] 
		] [
			return 1
		]
	]
]
; Noise frequency
psg_emulator/R6/bits: [true true true true true false false false]
; Channel A frequency
psg_emulator/R0/bits: [true true true true false false false false]  
psg_emulator/R1/bits: [false false false false false false false false]  
; Channel B frequency
psg_emulator/R2/bits: [true true true true true true true true]  
psg_emulator/R3/bits: [true false false false false false false false]  
; Channel C frequency
psg_emulator/R0/bits: [true true true true true true true true]  
psg_emulator/R5/bits: [true false false false false false false false]  
; Mixer
psg_emulator/R7/bits: [true true true false false true false false]


psg_emulator/R10/bits: [true true true true false true true true]  
psg_emulator/R11/bits: [true true true true false true true true]  
psg_emulator/R12/bits: [true true true true false true true true]  


psg_emulator/generate_sound 2

wait 0 ; nécessaire pour que le port sound fonctionne
sound-port: open sound://

print "Sound A"
insert sound-port psg_emulator/sample_channel_A
wait sound-port

print "Sound B"
insert sound-port psg_emulator/sample_channel_B
wait sound-port

print "Sound C"
insert sound-port psg_emulator/sample_channel_C
wait sound-port

print "Sound Mixed"
insert sound-port psg_emulator/sample_channel_mixed
wait sound-port


close sound-port


halt
guest23-Feb-2007/20:09:35+1:00
Ok, j'ai utilisé une onde carré et j'applique ta manip du DAA

donc je fais 128 + (7 * 7) la moitié de la phase
et 128 - (7 * 7) l'autre moitié

Mais ça résoud pas le problème de fond, ce ne sont pas les bonnes notes qui sont jouées.
guest23-Feb-2007/21:30:14+1:00
On avance, on avance

j'ai juste modifié 2 routines:

là pour générer une onde carrée
    ; build a wave at a frequency ans store it
   make-tone: func [freq /local note rep ret pitch phase] [
    	if not note: select head cache freq [
		pitch: to integer! sample/rate / freq  / 2 + 0.5
		note: make binary! pitch * 2
		insert/dup tail note to char! 128 + 25 pitch
		insert/dup tail note to char! 128 - 25 pitch
		insert cache freq
		insert cache head note
	]
        head note
        
    ]


et là , j'ai juste appliqué un coeff multiplicateur sur les fréquences des canaux pour changer d'octave, j'ai fait ça au jugé:

pcm: clear #{}
Fa': Fb': Fc': 0
foreach [A0 A1 B0 B1 C0 C1 noise mixer volA volB volC Ev0 Ev1 Shape JoyA JoyB] song [
	Fa: to integer! 20 * to integer! 0.5 + clock / (A1 and 15 * 256 + A0) 
	Fb: to integer! 20 * to integer! 0.5 + clock / (B1 and 15 * 256 + B0) 
	Fc: to integer! 20 * to integer! 0.5 + clock / (C1 and 15 * 256 + C0) 
	print [Fa Fb Fc]
	chg?: false
	if Fa' <> Fa [toneA: make-tone Fa chg?: true]
	if Fb' <> Fb [toneB: make-tone Fb chg?: true]
	if Fc' <> Fc [toneC: make-tone Fc chg?: true]
	insert tail pcm either chg? [prevmix: mix][prevmix]
	Fa': Fa Fb': Fb Fc': Fc
]
sample/data: pcm
insert sound-port sample

halt


2 remarques:
- Y'a un bruit de fond désagréable, je sais pas si c'est à cause du générateur d'ondes carrées ou d'un autre bug.

- On se rapproche de la musique originale mais ça joue encore faux, probablement que multiplier les fréquences pour changer d'octave c'est trop simple, y'a encore un truc qui m'échappe.
Goldevil4-Feb-2007/11:00:42+1:00
Tu avances.

Le bruit de fond est certainement du à l'onde carrée. Dans une onde carrée, y'a des plus hautes fréquences. En fait c'est comme s'il y avait des notes plus hautes jouées en même temps mais à un volume inférieur. Je suppose que l'étage analogique du MSX devait lisser le son avec quelques condensateurs et se rapprocher plus d'une onde sinusoïdale. Mais ça c'est difficile à simuler.

Si tu remets une onde sinusoïdale, tu arriveras à un son plus beau mais moins proche de l'original. Personnellement, je ne trouve pas cela plus mal.


Quand on y pense simuler, la génération du son d'un vieux circuit comme le PSG demande plus de travail qu'émuler des simples sons échantillonés d'une soundblaster de première génération.
guest24-Feb-2007/18:36:37+1:00
Bon alors en fait ça marche, c'est juste les valeurs extraites des régistres qui étaient fausses, donc mon élulateur Z80 est loin d'être au point.

Là j'intègre la gestion de l'amplitude, enveloppes et noise, et je prépare une demo
coccinelle4-Feb-2007/21:41:54+1:00
Pour revenir au sujet de départ, si j'ai bien compris, tu n'interprete pas directement le code binaire du Z80 mais tu le convertis d'abord en instruction Rebol.

Est-ce bien pour çà que tu n'as pas l'air de parser le code du programme binaire ?
guest24-Feb-2007/21:57:05+1:00
oui j'interprete pas directement les opcodes du Z80, je passe par une espèce de phase de parsing/compilation qui traduit de séquences ou carrément des routines entières en instructions Rebol.
Si je faisais pas ça mais de la vrai émulation ce serait encore plus lent.
Avec Rebcode je pourrais surement me permettre de faire de l'interprétation temps réelle mais j'ai pas testé.
coccinelle6-Feb-2007/8:49:47+1:00
Je ne suis pas aussi convaincu que toi en ce qui concerne la vitesse. Si tu générais du RebCode alors je serais vraiment d'accord mais sinon, j'ai un doute.

Faudrait faire la comparaison pour se rendre compte du gain ou de la perte.

Je vais y penser.

Login required to Post.


Powered by RebelBB and REBOL 2.7.8.4.2