[Thomson] SDDRIVE Vidéo
Modérateurs : Papy.G, fneck, Carl
Re: [Thomson] SDDRIVE Vidéo
En testant on trouve toujours des choses intéressantes !
J'ai fait ROR <$BF
(J'aurais pu faire LSR, le résultat serait identique)
Résultats :
- Ne marche plus du tout dans dcmoto
- Ne marche pas sur TO8D
- Marche sur MO5
Par rapport à INC, c'est exactement l'inverse. Donc on pourrait programmer INC ou ROR en fonction de la machine, et le programme fonctionnerait dans tous les cas. Reste à faire des tests sur T9000, TO7, TO7/70, MO5NR, MO6, TO9+, etc... Pour l'émulateur un petit patch devrait permettre de le faire fonctionner avec l'une ou l'autre instruction.
Cette solution est un peu trop hasardeuse pour la mettre en application dans l'EPROM officielle de SDDRIVE, mais c'est bon à savoir.
Il faut maintenant que je découvre s'il est possible d'arrêter la commande CMD17 après la lecture de 256 octets. Ce serait beaucoup plus propre et le gain serait énorme : gain de 4 cycles par bit. Pour 256 octets ça fait 8192 cycles !
[Edit 16:00]
D'après ce que je viens de lire la commande CMD17 (read single block) ne peut pas être interrompue. Par contre la commande CMD18 (read multiple block) peut être interrompue par la commande CMD12 (stop transmission). L'idée serait de remplacer CMD17 par CMD18, lire 256 octets, envoyer la commande CMD12 et ignorer les 256 octets suivants du bloc. A tester...
J'ai fait ROR <$BF
(J'aurais pu faire LSR, le résultat serait identique)
Résultats :
- Ne marche plus du tout dans dcmoto
- Ne marche pas sur TO8D
- Marche sur MO5
Par rapport à INC, c'est exactement l'inverse. Donc on pourrait programmer INC ou ROR en fonction de la machine, et le programme fonctionnerait dans tous les cas. Reste à faire des tests sur T9000, TO7, TO7/70, MO5NR, MO6, TO9+, etc... Pour l'émulateur un petit patch devrait permettre de le faire fonctionner avec l'une ou l'autre instruction.
Cette solution est un peu trop hasardeuse pour la mettre en application dans l'EPROM officielle de SDDRIVE, mais c'est bon à savoir.
Il faut maintenant que je découvre s'il est possible d'arrêter la commande CMD17 après la lecture de 256 octets. Ce serait beaucoup plus propre et le gain serait énorme : gain de 4 cycles par bit. Pour 256 octets ça fait 8192 cycles !
[Edit 16:00]
D'après ce que je viens de lire la commande CMD17 (read single block) ne peut pas être interrompue. Par contre la commande CMD18 (read multiple block) peut être interrompue par la commande CMD12 (stop transmission). L'idée serait de remplacer CMD17 par CMD18, lire 256 octets, envoyer la commande CMD12 et ignorer les 256 octets suivants du bloc. A tester...
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.
-
- Messages : 7987
- Inscription : 18 sept. 2010 12:08
- Localisation : Brest et parfois les Flandres
Re: [Thomson] SDDRIVE Vidéo
Mais interrompu n'importe quand ou uniquement à la frontière de blocs ?CMD18 peut être interrompu.
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
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Re: [Thomson] SDDRIVE Vidéo
Le moment précis de l'interruption est l'une des questions qui se posent, mais pas la seule. La documentation est très pauvre sur le sujet.
J'ai modifié la lecture des blocs Thomson de 256 octets :
- Remplacement de la commande CMD17 (lecture d'un bloc) par CMD18 (lecture multibloc)
- Lecture de 256 octets
- Envoi de la commande CMD12 (stop transmission)
Résultat :
- Le programme de sélection SDDRIVE.SEL se charge bien (normal, il lit uniquement des blocs de 512 octets)
- Le programme SDDRIVE Vidéo (premier algorithme de Daniel) se lance et fonctionne, mais l'image est décalée vers le bas d'environ 1/4 d'écran, et la partie basse s'affiche en haut.
- Le programme SDDRIVE Vidéo (dernier algorithme de __sam__) plante dès le début.
Les deux derniers points s'expliquent facilement par un décalage d'un bloc (ou deux) dans la lecture des données.
Il faut savoir que la transmission SPI avec la carte SD est bidirectionnelle. A chaque accès à <$BF un bit est lu et un bit est écrit. Par contre le 6809 n'est pas bidirectionnel : s'il écrit, il perd le bit envoyé par la carte, s'il lit il ne maîtrise pas le bit écrit vers la carte qui est forcé à 1 par l'électronique de SDDRIVE.
Donc, pendant l'envoi de la commande CMD12, la carte continue à envoyer des bits, qui sont ignorés par le 6809. A quel moment elle s'arrête, c'est un grand mystère. Peut-être dès la fin de réception de CMD0, soit vers le 262ème octet du bloc, soit en fin de bloc. La commande CMD17 incrémente automatiquement le LBA (adresse physique dans la carte) après chaque lecture de bloc. La commande CMD18 le fait-elle aussi ? (en particulier si elle est interrompue au milieu d'un bloc ?).
Théoriquement la commande CMD12 envoie des octets à zéro tant que le processus d'interruption n'est pas terminé, puis des octets à $FF (carte prête). J'ai testé les octets à zéro pour ne pas continuer le programme tant que la carte n'est pas prête, mais ça ne change rien. Il est possible que CMD18 continue à envoyer les derniers octets du bloc et ils sont à $FF, donc on croit la carte prête mais en fait non, elle continue à lire le bloc.
Ce ne sont que des suppositions, il faudrait des heures et des heures de test pour arriver à tout comprendre. En plus rien ne prouve que tous les modèles de cartes ont un comportement identique, puisque les spécifications officielles sont muettes sur ce sujet.
J'ai modifié la lecture des blocs Thomson de 256 octets :
- Remplacement de la commande CMD17 (lecture d'un bloc) par CMD18 (lecture multibloc)
- Lecture de 256 octets
- Envoi de la commande CMD12 (stop transmission)
Résultat :
- Le programme de sélection SDDRIVE.SEL se charge bien (normal, il lit uniquement des blocs de 512 octets)
- Le programme SDDRIVE Vidéo (premier algorithme de Daniel) se lance et fonctionne, mais l'image est décalée vers le bas d'environ 1/4 d'écran, et la partie basse s'affiche en haut.
- Le programme SDDRIVE Vidéo (dernier algorithme de __sam__) plante dès le début.
Les deux derniers points s'expliquent facilement par un décalage d'un bloc (ou deux) dans la lecture des données.
Il faut savoir que la transmission SPI avec la carte SD est bidirectionnelle. A chaque accès à <$BF un bit est lu et un bit est écrit. Par contre le 6809 n'est pas bidirectionnel : s'il écrit, il perd le bit envoyé par la carte, s'il lit il ne maîtrise pas le bit écrit vers la carte qui est forcé à 1 par l'électronique de SDDRIVE.
Donc, pendant l'envoi de la commande CMD12, la carte continue à envoyer des bits, qui sont ignorés par le 6809. A quel moment elle s'arrête, c'est un grand mystère. Peut-être dès la fin de réception de CMD0, soit vers le 262ème octet du bloc, soit en fin de bloc. La commande CMD17 incrémente automatiquement le LBA (adresse physique dans la carte) après chaque lecture de bloc. La commande CMD18 le fait-elle aussi ? (en particulier si elle est interrompue au milieu d'un bloc ?).
Théoriquement la commande CMD12 envoie des octets à zéro tant que le processus d'interruption n'est pas terminé, puis des octets à $FF (carte prête). J'ai testé les octets à zéro pour ne pas continuer le programme tant que la carte n'est pas prête, mais ça ne change rien. Il est possible que CMD18 continue à envoyer les derniers octets du bloc et ils sont à $FF, donc on croit la carte prête mais en fait non, elle continue à lire le bloc.
Ce ne sont que des suppositions, il faudrait des heures et des heures de test pour arriver à tout comprendre. En plus rien ne prouve que tous les modèles de cartes ont un comportement identique, puisque les spécifications officielles sont muettes sur ce sujet.
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.
-
- Messages : 7987
- Inscription : 18 sept. 2010 12:08
- Localisation : Brest et parfois les Flandres
Re: [Thomson] SDDRIVE Vidéo
Oui je crois que la solution standard avec CMD17 est la plus fiable.
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
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Re: [Thomson] SDDRIVE Vidéo
@sam : Oui, je pense comme toi et je préfère garder la version actuelle (20180702).
Le processus de mise au point a été long et laborieux mais le résultat est stable. Depuis début juillet je n'ai pas fait une seule modification.
L'utilisation de la commande CMD18 pour lire seulement 256 octets par bloc risque d'apporter des bugs, et si l'interruption par CMD12 intervient seulement à la fin d'un bloc on ne gagnera rien. Le contrôleur SDDRIVE charge déjà n'importe quel jeu trois ou quatre fois plus vite que le contrôleur de disquette, c'est bien suffisant.
Quand aux démonstrations en streaming (musique et vidéo), elles lisent des blocs de 512 octets avec la commande CMD18. L'amélioration de la lecture des secteurs Thomson de 256 octets n'a aucun impact sur leur débit.
Le processus de mise au point a été long et laborieux mais le résultat est stable. Depuis début juillet je n'ai pas fait une seule modification.
L'utilisation de la commande CMD18 pour lire seulement 256 octets par bloc risque d'apporter des bugs, et si l'interruption par CMD12 intervient seulement à la fin d'un bloc on ne gagnera rien. Le contrôleur SDDRIVE charge déjà n'importe quel jeu trois ou quatre fois plus vite que le contrôleur de disquette, c'est bien suffisant.
Quand aux démonstrations en streaming (musique et vidéo), elles lisent des blocs de 512 octets avec la commande CMD18. L'amélioration de la lecture des secteurs Thomson de 256 octets n'a aucun impact sur leur débit.
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.
Re: [Thomson] SDDRIVE Vidéo
Bonjour Daniel,
Tes citations m'intriguent...
ARDDRIVE pour Arduino Drive ? Lecture de la carte SD accélérée par un Arduino ?
J'ai déjà pensé à un truc qu'avec des boitiers logiques (et donc pas d'Arduino) pour effectivement faire x 8 sur le débit, mais je n'ai pas trouvé le temps de faire le schéma et encore moi de prototyper
L'idée est d'implanter un multiplicateur de fréquence x8 (principe ici) à partir de l'horloge E et d'utiliser des registres à décalage sur MISO et MOSI cadencés par l'horloge à 8 MHz. Ainsi en une seule écriture sur le bus de données (et en lecture dans l'autre sens) avec les 8 bits utilisés permettrait d'accélérer encore SDDRIVE. La difficulté risque peut-être d'être liée à la montée en fréquence, au niveau du design du schéma et du PCB, déjà que la mise au point de la version actuelle n'a pas été de tout repos
Sylvain
Re: [Thomson] SDDRIVE Vidéo
Mon projet est effectivement d'utiliser un Arduino comme interface avec la carte SD. Le contrôleur serait sur le même principe que SDDRIVE, mais chaque lecture ou écriture transférerait 8 bits au lieu de 1. Reste à vérifier si l'Arduino est assez rapide. Par manque de temps le projet n'avance pas vite...
Les registres à décalage sont aussi une piste intéressante à explorer. La mise au point de l'électronique n'est pas forcément facile à ces fréquences élevées, mais sur le papier ça devrait marcher.
Les registres à décalage sont aussi une piste intéressante à explorer. La mise au point de l'électronique n'est pas forcément facile à ces fréquences élevées, mais sur le papier ça devrait marcher.
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.
-
- Messages : 7987
- Inscription : 18 sept. 2010 12:08
- Localisation : Brest et parfois les Flandres
Re: [Thomson] SDDRIVE Vidéo
J'ai introduit un filtre temporel dans l'outil de conversion. J'ai l'impression qu'il réduit visuellement l'effet de "vague" communément appelé "tearing-effect" en vidéo.
J'ai aussi découvert le projet LUA-JIT , qui offrent un interpréteur LUA bien plus rapide que celui de base. Il est certes un peu plus gros (400ko l'exe au lieu de 200ko pour l'interprète de base), mais grâce à lui j'encode en x3 au lieu de x1.6. A la louche le programmes LUA tournent deux à trois fois plus rapidement avec lui. Il faut compter un peu au dessus d'une minute pour un vidéo-clip standard. C'est cool! (executables windows et annexes pour lancer le script)
J'ai testé ce filtre vidéo avec des clips bien pêchus principalement à base de bon gros rock qui tâche (mais pas que ca) et je trouve que ca ressort très bien au final . En outre question audio ca me permet aussi d’entendre que les aigus passent bien en dépit des pauvre 5khz du player.
Notes:
(1) les liens sont temporaires (durée 1 mois environ)
(2) vidéos avec un filtre temporal différent ainsi qu'un niveau de "dither" supplémentaire permettant de voir un peu plus de détails sombres (particulièrement dans le clip "Thriller"): >>ici<<
Code : Tout sélectionner
-- conversion fichier video en fichier sd-drive
--
-- version alpha 0.05
---
-- Samuel DEVULDER Aout-Sept 2018
-- code experimental. essaye de determiner
-- les meilleurs parametres (fps, taille ecran
-- pour respecter le fps ci-dessous.
-- gray peut être true ou false suivant qu'on
-- veut une sortie couleur ou pas. Le gris est
-- generalement plus rapide et/ou avec un ecran
-- plus large. Si on le laisse à nil, l'outil
-- détermine automatiquement le mode couleur de
-- la video.
-- Work in progress!
-- =================
-- le code doit être nettoye et rendu plus
-- amical pour l'utilisateur
local function round(x)
return math.floor(x+.5)
end
local BUFFER_SIZE = 4096
local FPS_MAX = 30
local VIDEO_FILTER = {1,4,1} -- {1,4,10,4,1} -- {1,6,42,6,1} -- {1,4,10,30,10,4,1}
local tmp = 'tmp'
local img_pattern = tmp..'/img%05d.bmp'
local cycles = 199 -- cycles par échantillons audio
local hz = round(8000000/cycles)/8
local fps = 13
local gray = nil
local interlace = false --gray
local dither = 1
local ffmpeg = 'tools\\ffmpeg'
local mode = 'p'
local file = arg[1]:gsub('^/cygdrive/(%w)/','%1:/')
local function exists(file)
local ok, err, code = os.rename(file, file)
if not ok then
if code == 13 then
-- Permission denied, but it exists
return true
end
end
return ok, err
end
local function isdir(file)
return exists(file..'/')
end
if not exists(file) then os.exit(0) end
local function percent(x)
return round(math.min(1,x)*100)
end
local function hms(secs, fmt)
secs = round(secs)
return string.format(fmt or "%d:%02d:%02d",
math.floor(secs/3600), math.floor(secs/60)%60, math.floor(secs)%60)
end
-- if file:match('miga') then
-- gray = false
-- interlace = false
-- end
-- nom fichier
io.stdout:write('\n'..file..'\n')
io.stdout:flush()
-- recherche la bonne taille d'image
local x,y = 80,45
local IN,line = assert(io.popen(ffmpeg..' -i "'..file ..'" 2>&1', 'r'))
for line in IN:lines() do
local h,m,s = line:match('Duration: (%d+):(%d+):(%d+%.%d+),')
if h and m and s then duration = h*3600 + m*60 +s end
local a,b = line:match(', (%d+)x(%d+)')
if a and b then x,y=a,b end
end
IN:close()
if not duration then error("Can't get duration!") end
local max_ar
for i=2,10 do
local t = x*i/y
t = math.abs(t-round(t))
if max_ar==nil or t<max_ar then
max_ar = t
aspect_ratio = round(x*i/y)..':'..i
end
end
local w = 80
local h = round(w*y/x)
if h>50 then
h = 50
w = round(h*x/y)
end
if mode==nil then
mode = (h*w>32*48 and 'i') or 'p'
end
-- initialise la progression dans les octets de l'image
local lines,indices = {},{}
for i=math.floor((50-h)/2)*4,(math.floor((50-h)/2)+h)*4-1 do
if i%4<3 then
table.insert(lines, i)
end
end
if mode=='p' or mode=='a' then
-- rien
elseif mode=='i' then
local t = {}
for i=1,#lines,2 do
table.insert(t, lines[i])
end
for i=2,#lines,2 do
table.insert(t, lines[i])
end
lines = t
elseif mode=='2' then
local t = {}
for i=1,#lines,3 do
table.insert(t, lines[i])
end
for i=2,#lines,3 do
table.insert(t, lines[i])
end
table.sort(t)
for i=3,#lines,3 do
table.insert(t, lines[i])
end
lines = t
elseif mode=='3' then
local t = {}
for i=2,#lines,3 do
table.insert(t, lines[i])
end
for i=1,#lines,3 do
table.insert(t, lines[i])
end
for i=2,#lines,3 do
table.insert(t, lines[i])
end
lines = t
elseif mode=='ipi' then
local t = {}
for i=1+math.floor(#lines/3),math.floor(2*#lines/3) do
table.insert(t, lines[i])
end
for i=1,math.floor(#lines/3),2 do
table.insert(t, lines[i])
end
for i=1+math.floor(2*#lines/3),#lines,2 do
table.insert(t, lines[i])
end
for i=2,math.floor(#lines/3),2 do
table.insert(t, lines[i])
end
for i=2+math.floor(2*#lines/3),#lines,2 do
table.insert(t, lines[i])
end
lines = t
elseif mode=='r' then
local size = #lines
for i = size, 1, -1 do
local rand = math.random(size)
lines[i], lines[rand] = lines[rand], lines[i]
end
elseif mode=='3i' then
-- 14 + 14+12
local w = 2+4+4+4+4+4
local x = math.floor((39-w)/2)
for _,j in ipairs(lines) do
for i=j*40+x,j*40+x+w-1 do
table.insert(indices,i)
end
end
for j=1,#lines,2 do
for i=lines[j]*40,lines[j]*40+x-1 do
table.insert(indices,i)
end
for i=lines[j]*40+x+w,lines[j]*40+39 do
table.insert(indices,i)
end
end
for j=2,#lines,2 do
for i=lines[j]*40,lines[j]*40+x-1 do
table.insert(indices,i)
end
for i=lines[j]*40+x+w,lines[j]*40+39 do
table.insert(indices,i)
end
end
lines = {}
elseif mode=='d' then
lines = {}
for x=0,39 do
for y=0,math.min(24,x) do
local p = y*320+(x-y)
for j=p,p+319,40 do
table.insert(indices,j)
end
end
end
for y=1,24 do
for x=0,24-y do
local p = (y+x)*320+39-x
for j=p,p+319,40 do
table.insert(indices,j)
end
end
end
else
error('Unknown mode: ' .. mode)
end
for _,i in ipairs(lines) do
-- print(i)
for j=i*40,i*40+39 do
table.insert(indices, j)
end
end
-- os.exit(0)
-- flux audio
local AUDIO = {}
function AUDIO:new(file, hz)
local o = {
stream = assert(io.popen(ffmpeg..' -i "'..file ..'" -v 0 -f u8 -ac 1 -ar '..round(8*hz)..' -acodec pcm_u8 pipe:', 'rb')),
cor = {8,255}, -- volume auto
buf = '', -- buffer
running = true
}
setmetatable(o, self)
self.__index = self
return o
end
function AUDIO:close()
self.stream:close()
end
function AUDIO:next_sample()
local buf = self.buf
if buf:len()<=8 then
local t = self.stream:read(BUFFER_SIZE)
if not t then
self.running = false
t = string.char(0,0,0,0,0,0,0,0)
end
buf = buf .. t
end
local v = (buf:byte(1) + buf:byte(2) + buf:byte(3) + buf:byte(4) +
buf:byte(5) + buf:byte(6) + buf:byte(7) + buf:byte(8))*.125
self.buf = buf:sub(9)
-- auto volume
if v<self.cor[2] then self.cor[2]=v end
v = v-self.cor[2]
if v*self.cor[1]>255 then self.cor[1]=255/v end
v = v*self.cor[1]
-- dither
v = math.max(math.min(v/4 + math.random() + math.random() - 1, 63),0)
return math.floor(v)
end
-- filtre video
local FILTER = {}
function FILTER:new(weights)
local o = {w={}, t=nil, size=#weights}
setmetatable(o, self)
self.__index = self
local total = 0
for _,w in ipairs(weights) do total = total + w end
for i=1,o.size do o.w[i] = weights[i]/total end
if o.size==1 then
function o:byte(offset)
return self.t[1][offset]
end
elseif o.size==2 then
function o:byte(offset)
return round(
self.t[2][offset]*self.w[2] +
self.t[1][offset]*self.w[1])
end
elseif o.size==3 then
function o:byte(offset)
return round(
self.t[3][offset]*self.w[3] +
self.t[2][offset]*self.w[2] +
self.t[1][offset]*self.w[1])
end
elseif o.size==4 then
function o:byte(offset)
return round(
self.t[4][offset]*self.w[4] +
self.t[3][offset]*self.w[3] +
self.t[2][offset]*self.w[2] +
self.t[1][offset]*self.w[1])
end
elseif o.size==5 then
function o:byte(offset)
return round(
self.t[5][offset]*self.w[5] +
self.t[4][offset]*self.w[4] +
self.t[3][offset]*self.w[3] +
self.t[2][offset]*self.w[2] +
self.t[1][offset]*self.w[1])
end
elseif o.size==6 then
function o:byte(offset)
return round(
self.t[6][offset]*self.w[6] +
self.t[5][offset]*self.w[5] +
self.t[4][offset]*self.w[4] +
self.t[3][offset]*self.w[3] +
self.t[2][offset]*self.w[2] +
self.t[1][offset]*self.w[1])
end
elseif o.size==7 then
function o:byte(offset)
return round(
self.t[7][offset]*self.w[7] +
self.t[6][offset]*self.w[6] +
self.t[5][offset]*self.w[5] +
self.t[4][offset]*self.w[4] +
self.t[3][offset]*self.w[3] +
self.t[2][offset]*self.w[2] +
self.t[1][offset]*self.w[1])
end
elseif o.size==8 then
function o:byte(offset)
return round(
self.t[8][offset]*self.w[8] +
self.t[7][offset]*self.w[7] +
self.t[6][offset]*self.w[6] +
self.t[5][offset]*self.w[5] +
self.t[4][offset]*self.w[4] +
self.t[3][offset]*self.w[3] +
self.t[2][offset]*self.w[2] +
self.t[1][offset]*self.w[1])
end
end
return o
end
function FILTER:push(bytecode)
local t,insert,byte={},table.insert,string.byte
for i=1,bytecode:len() do insert(t, byte(bytecode,i)) end
if not self.t then
self.t = {}
for i=1,self.size do insert(self.t, t) end
end
table.remove(self.t, 1)
table.insert(self.t, t)
return self
end
function FILTER:byte(offset)
local val = 0.5
for i=1,#self.t do val = val + self.w[i]*self.t[i][offset] end
return math.floor(val)
end
-- o.filter:new{1,4,10,30,10,4,1} -- {1,2,4,2,1} -- {1,4,10,4,1} -- {1,2,6,2,1} -- {1,1,2,4,2,1,1} -- {1,2,3,6,3,2,1} -- ,2,4,8,16,32}
-- flux video
local VIDEO = {}
function VIDEO:new(file, w, h, fps, gray)
if isdir(tmp) then
os.execute('del >nul /s /q '..tmp)
else
os.execute('md '..tmp)
end
local o = {
cpt = 1, -- compteur image
width = w,
height = h,
fps = fps or 10,
gray = gray or false,
image = {},
dither = nil,
expected_size = 54 + h*(math.floor((w*3+3)/4)*4),
running=true,
streams = {
inp = assert(io.open(file, 'rb')),
out = assert(io.popen(ffmpeg..' -i pipe: -v 0 -r '..fps..' -s '..w..'x'..h..' -an '..img_pattern, 'wb')),
}
}
setmetatable(o, self)
self.__index = self
for i=0,7999+3 do o.image[i]=0 end
o.filter = FILTER:new(VIDEO_FILTER)
return o
end
function VIDEO:close()
if io.type(self.streams.inp)=='file' then self.streams.inp:close() end
if io.type(self.streams.out)=='file' then self.streams.out:close() end
end
function VIDEO:init_dither()
local function bayer(t)
local m=#t
local n=#t[1]
local d={}
for i=1,2*m do
d[i] = {}
for j=1,2*n do
d[i][j] = 0
end
end
for i=1,m do
for j=1,n do
local z = 4*t[i][j]
d[m*0+i][n*0+j] = z-3
d[m*1+i][n*1+j] = z-2
d[m*1+i][n*0+j] = z-1
d[m*0+i][n*1+j] = z-0
end
end
return d
end
local m = {{1}}
-- m={{1,3},{3,1}}
for i=1,dither do m = bayer(m) end
local x = 0
for i=1,#m do
for j=1,#m[1] do
x = math.max(x, m[i][j])
end
end
x = 1/(x + 1)
for i = 1,#m do
for j = 1,#m[1] do
m[i][j] = m[i][j]*x
end
end
m.w = #m
m.h = #m[1]
function m:get(i,j)
return self[1+(i % self.w)][1+(j % self.h)]
end
self.dither = m
end
function VIDEO:linear(u)
return u<0.04045 and u/12.92 or (((u+0.055)/1.055)^2.4)
end
function VIDEO:pset(x,y, r,g,b)
if not self._linear then
self._linear = {}
for i=0,255 do self._linear[i] = self:linear(i/255) end
end
r,g,b = self._linear[r],self._linear[g],self._linear[b]
if not self.dither then VIDEO:init_dither() end
local d = self.dither:get(x,y)
if not self._pset then
self._pset = {}
self._pset[0] = {}
self._pset[1] = {}
for i=0,15 do
self._pset[0][i] = {}
self._pset[1][i] = {}
for j=0,3 do
self._pset[0][i][j] = (i%4) + 4*j
self._pset[1][i][j] = (i-(i%4)) + j
end
end
end
local o,p = x%2,math.floor(x/2) + y*160
local function v(v)
-- assert(0<=v and v<=3, 'v=' .. v)
self.image[p] = self._pset[o][self.image[p]][v]
p = p+40
end
if interlace then
local q = self.cpt%2 == 0
function v(v)
if q then
self.image[p] = self._pset[o][self.image[p]][v]
q = false
else
q = true
end
p = p+40
end
end
if self.gray then
r = (.2126*r + .7152*g + .0722*b)*9 + d
if r>=4 then v(3)
elseif r>=2 then v(2)
elseif r>=1 then v(1)
else v(0)
end
if r>=7 then v(3)
elseif r>=5 then v(2)
elseif r>=3 then v(1)
else v(0)
end
if r>=9 then v(3)
elseif r>=8 then v(2)
elseif r>=6 then v(1)
else v(0)
end
else
v(math.floor(r*3 + d))
v(math.floor(g*3 + d))
v(math.floor(b*3 + d))
end
end
function VIDEO:read_bmp(bytecode) -- (https://www.gamedev.net/forums/topic/572784-lua-read-bitmap/)
-- Helper function: Parse a 16-bit WORD from the binary string
local function ReadWORD(str, offset)
local loByte = str:byte(offset);
local hiByte = str:byte(offset+1);
return hiByte*256 + loByte;
end
-- Helper function: Parse a 32-bit DWORD from the binary string
local function ReadDWORD(str, offset)
local loWord = ReadWORD(str, offset);
local hiWord = ReadWORD(str, offset+2);
return hiWord*65536 + loWord;
end
-------------------------
-- Parse BITMAPFILEHEADER
-------------------------
local offset = 1;
local bfType = ReadWORD(bytecode, offset);
if(bfType ~= 0x4D42) then
error("Not a bitmap file (Invalid BMP magic value)");
return;
end
local bfOffBits = ReadWORD(bytecode, offset+10);
-------------------------
-- Parse BITMAPINFOHEADER
-------------------------
offset = 15; -- BITMAPFILEHEADER is 14 bytes long
local biWidth = ReadDWORD(bytecode, offset+4);
local biHeight = ReadDWORD(bytecode, offset+8);
local biBitCount = ReadWORD(bytecode, offset+14);
local biCompression = ReadDWORD(bytecode, offset+16);
if(biBitCount ~= 24) then
error("Only 24-bit bitmaps supported (Is " .. biBitCount .. "bpp)");
return;
end
if(biCompression ~= 0) then
error("Only uncompressed bitmaps supported (Compression type is " .. biCompression .. ")");
return;
end
---------------------
-- Parse bitmap image
---------------------
local ox = math.floor((80 - biWidth)/4)*2
local oy = math.floor((50 - biHeight)/2)
local oo = 4*math.floor((biWidth*biBitCount/8 + 3)/4)
local pr = self.filter:push(bytecode)
for y = biHeight-1, 0, -1 do
offset = bfOffBits + oo*y + 1;
for x = ox, ox+biWidth-1 do
self:pset(x, oy,
pr:byte(offset+2), -- r
pr:byte(offset+1), -- g
pr:byte(offset ) -- b
);
offset = offset + 3;
end
oy = oy+1
end
end
function VIDEO:next_image()
if not self.running then return end
-- nom nouvelle image
local name = img_pattern:format(self.cpt); self.cpt = self.cpt + 1
local buf = ''
local f = io.open(name,'rb')
if f then
buf = f:read(self.expected_size) or buf
f:close()
end
-- si pas la bonne taille, on nourrit ffmpeg
-- jusqu'a obtenir un fichier BMP complet
local timeout = 5
while buf:len() ~= self.expected_size and timeout>0 do
buf = self.streams.inp:read(BUFFER_SIZE)
if buf then
self.streams.out:write(buf)
self.streams.out:flush()
else
if io.type(self.streams.out)=='file' then
self.streams.out:close()
end
-- io.stdout:write('wait ' .. name ..'\n')
-- io.stdout:flush()
local t=os.time()+1
repeat until os.time()>t
timeout = timeout - 1
end
f = io.open(name,'rb')
if f then
buf = f:read(self.expected_size) or ''
f:close()
timeout = 5
else
buf = ''
end
end
-- effacement temporaire
os.remove(name)
if buf and buf:len()>0 then
-- nettoyage de l'image précédente
-- for i=0,7999+3 do self.image[i]=0 end
-- lecture image
self:read_bmp(buf)
else
if self.cpt==2 and not self._avi_hack then
self:close()
self.streams.inp = assert(io.popen(ffmpeg..' -i "'..file..'" -v 0 -f avi pipe:','rb'))
self.streams.out = assert(io.popen(ffmpeg..' -i pipe: -v 0 -r '..self.fps..' -s '..self.width..'x'..self.height..' -an '..img_pattern, 'wb'))
self._avi_hack = true
self.cpt = 1
self:next_image()
else
self.running = false
end
end
end
function VIDEO:skip_image()
local bak = self.pset
self.pset = function() end
self:next_image()
self.pset = bak
end
-- auto determination des parametres
local stat = VIDEO:new(file,w,h,round(fps/2),gray)
stat.super_pset = stat.pset
stat.histo = {n=0}; for i=0,255 do stat.histo[i]=0 end
function stat:pset(x,y, r,g,b)
self.histo[r] = self.histo[r]+1
self.histo[g] = self.histo[g]+1
self.histo[b] = self.histo[b]+1
self:super_pset(x,y,r,g,b)
if gray==nil then
if self.mnt==nil then
self.mnt = {n=0,r1=0,g1=0,b1=0,r2=0,g2=0,b2=0}
end
local m = math.max(r,g,b)
if m>10 then
m=1/m
r,g,b = r*m,g*m,b*m
m = self.mnt
m.n = m.n + 1
m.r1 = m.r1 + r
m.g1 = m.g1 + g
m.b1 = m.b1 + b
m.r2 = m.r2 + r*r
m.g2 = m.g2 + g*g
m.b2 = m.b2 + b*b
end
end
end
stat.super_next_image = stat.next_image
stat.mill = {'|', '/', '-', '\\'}
stat.mill[0] = stat.mill[4]
function stat:next_image()
self:super_next_image()
io.stderr:write(string.format('> analyzing...%s %d%%\r', self.mill[self.cpt % 4], percent(self.cpt/self.fps/duration)))
io.stderr:flush()
end
stat.trames = 0
stat.prev_img = {}
for i=0,7999 do stat.prev_img[i]=-1 end
function stat:count_trames()
local pos,prev,curr = 0,stat.prev_img,stat.image
local chg = 0
for _,i in ipairs(indices) do
if prev[i] ~= curr[i] then chg = chg+1 end
end
for _,i in ipairs(indices) do
-- for i=0,7999 do
if prev[i] ~= curr[i] then
stat.trames = stat.trames + 1
local k = i - pos
if k<0 then k=8000 end
if k<=2 then
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
elseif k<=256 then
pos = i
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
else
pos = i
prev[pos] = curr[pos]; pos = pos+1
end
end
end
end
while stat.running do
stat:next_image()
stat:count_trames()
end
io.stderr:write(string.rep(' ',79)..'\r')
io.stderr:flush()
-- determine if monochrome
if gray==nil then
local m = stat.mnt
m.r1,m.g1,m.b1 = m.r1/m.n,m.g1/m.n,m.b1/m.n
m.r2,m.g2,m.b2 = m.r2/m.n,m.g2/m.n,m.b2/m.n
local e = 0
e = e + math.sqrt(m.r2 - m.r1*m.r1)
e = e + math.sqrt(m.g2 - m.g1*m.g1)
e = e + math.sqrt(m.b2 - m.b1*m.b1)
e = e/3
gray = e<.07
-- print(gray,e)
-- if not gray then os.exit() end
end
local max_trames = 1000000/fps/cycles
local avg_trames = (stat.trames/stat.cpt) * 1.08 -- 001 -- 0.11% safety margin
local ratio = max_trames / avg_trames
-- print(ratio)
if ratio>1 then
fps = math.min(math.floor(fps*ratio),interlace and 2*FPS_MAX or FPS_MAX)
elseif ratio<1 then
local zoom = ratio^.5
w=math.floor(w*zoom)
h=math.floor(h*zoom)
end
stat.total = 0
for i=1,255 do
stat.total = stat.total + stat.histo[i]
end
stat.threshold_min = (gray and .03 or .05)*stat.total
stat.min = 0
for i=1,255 do
stat.min = stat.min + stat.histo[i]
if stat.min>stat.threshold_min then
stat.min = i-1
break
end
end
stat.max = 0
stat.threshold_max = (gray and .03 or .05)*stat.total
for i=254,1,-1 do
stat.max = stat.max + stat.histo[i]
if stat.max>stat.threshold_max then
stat.max = i+1
break
end
end
-- print(stat.min, stat.max)
-- io.stdout:flush()
local video_cor = {stat.min, 255/(stat.max - stat.min)}
-- fichier de sortie
function file_content(file, size)
local INP = assert(io.open(file, 'rb'))
local buf = ''
while true do
local t = INP:read(256)
if not t then break end
buf = buf .. t .. string.rep(string.char(0),512-t:len())
end
size = size - buf:len()
if size<0 then
print('size',size)
error('File ' .. file .. ' is too big')
end
return buf .. string.rep(string.char(0),size)
end
local OUT = assert(io.open(file:gsub('.*[/\\]',''):gsub('%.[%a%d]+','')..'.sd', 'wb'))
OUT:write(file_content('bin/bootblk.raw', 512))
OUT:write(file_content(gray and 'bin/player1.raw' or 'bin/player0.raw', 7*512))
-- flux audio/video
local audio = AUDIO:new(file, hz)
local video = VIDEO:new(file,w,h,fps,gray)
-- adaptation luminosité
video.super_pset = video.pset
function video:pset(x,y, r,g,b)
local function f(x)
x = round((x-video_cor[1])*video_cor[2]);
return x<0 and 0 or x>255 and 255 or x
end
self:super_pset(x,y, f(r),f(g),f(b))
end
-- vars pour la cvonvesion
local start = os.time()
local tstamp = 0
local cycles_per_img = 1000000 / fps
local current_cycle = 0
local completed_imgs = 0
local pos = 8000
local blk = ''
-- init previous image
local curr = video.image
local prev = {}
for i=0,7999+3 do prev[i] = -1 end
function test_fin_bloc()
if blk:len()==3*170 then
local s1 = audio:next_sample()
local s2 = audio:next_sample()
local s3 = audio:next_sample()
local t = s1*1024 + math.floor(s2/2)*32 + math.floor(s3/2)
blk = blk .. string.char(math.floor(t/256), t%256)
OUT:write(blk)
blk = ''
current_cycle = current_cycle + cycles*3
end
end
local _recalc_indices_d = {}
for i=0,255 do
local a = {i % 4, math.floor(i/4)%4}
local b = {math.floor(i/16)%4, math.floor(i/64)%4}
_recalc_indices_d[i] = math.abs(a[1]-b[1]) + math.abs(a[2]-b[2])
end
function recalc_indices_d()
local d = {}
for i=0,7999,160 do
local e,f=0,0
for j=i,i+119 do
if curr[j]~=prev[j] then
f = f + 1
e = e + (_recalc_indices_d[prev[j]*16+curr[j]] or 6)
end
end
table.insert(d, {e=e/(f>0 and f or 1), i=i})
end
local MAX_DELAY = 1000000/fps -- 100*1000 -- µs
local e,h = 0,math.floor(MAX_DELAY *8 / (w*3*cycles))
local PRE = math.floor(h/3)
-- print(h,PRE)
for i=PRE,math.min(PRE+h,50) do e=e+d[i].e end
local m,p = e,1
for i=PRE+1,50-h do
e = e - d[i-1].e + d[i+h].e
if e>m then m,p = e,i-PRE end
end
-- print(p,m)
indices = {}
for i=p,50 do
for j=d[i].i,d[i].i+119 do table.insert(indices, j) end
end
for i=1,p-1 do
for j=d[i].i,d[i].i+119 do table.insert(indices, j) end
end
end
-- conversion
io.stdout:write(string.format('> %dx%d %s (%s) %s at %d fps (%d%% zoom, %s)\n',
w, h, mode, aspect_ratio,
hms(duration, "%dh %dm %ds"), fps, percent(math.max(w/80,h/50)), gray and "gray" or "color"))
io.stdout:flush()
video:next_image()
current_cycle = current_cycle + cycles_per_img
video:next_image()
while audio.running do
-- infos
if video.cpt % video.fps == 0 then
tstamp = tstamp + 1
local d = os.time() - start
local t = "> %d%% %s (%3.1fx) e=%5.3f a=(x%+d)*%.1g"
t = t:format(
percent(tstamp/duration), hms(tstamp),
round(100*tstamp/(d==0 and 100000 or d))/100, completed_imgs/video.cpt,
-audio.cor[2], audio.cor[1]
)
local etc = round(d*(duration-tstamp)/tstamp/5)*5
etc = etc>0 and d>15 and "ETC="..hms(etc) or ""
t = t .. string.rep(' ', math.max(0,79-t:len()-etc:len())) .. etc .. "\r"
io.stdout:write(t)
io.stdout:flush()
end
if mode=='a' then recalc_indices_d() end
for _,i in ipairs(indices) do
-- for i=0,7999 do
if prev[i] ~= curr[i] then
local k = i - pos
if k<0 then k=8000 end
pos = i
local buf = {audio:next_sample()*4,0,0}
-- if mode=='i' and w==80 then
-- if k<=2 and ((pos-k)%40)+4>=40
-- end
if k<=2 then
-- deplacement trop faible: mise a jour des 4 octets
-- videos suivants d'un coup
pos = pos - k
buf[1] = buf[1] + 1
buf[2] = curr[pos+0]*16 + curr[pos+1]
buf[3] = curr[pos+2]*16 + curr[pos+3]
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
elseif k<=256 then
-- deplacement 8 bit
buf[1] = buf[1] + 0
buf[2] = k%256
buf[3] = curr[pos+0]*16 + curr[pos+1]
prev[pos] = curr[pos]; pos = pos+1
prev[pos] = curr[pos]; pos = pos+1
else
-- deplacement arbitraire
buf[1] = buf[1] + 2 + math.floor(pos/4096)
buf[2] = math.floor(pos/16) % 256
buf[3] = (pos%16)*16 + curr[pos]
prev[pos] = curr[pos]; pos = pos + 1
end
blk = blk .. string.char(buf[1], buf[2], buf[3])
current_cycle = current_cycle + cycles
test_fin_bloc()
end
end
completed_imgs = completed_imgs + 1
-- skip image if drift is too big
while current_cycle>2*cycles_per_img do
video:skip_image()
if video.cpt % video.fps == 0 then
tstamp = tstamp + 1
end
current_cycle = current_cycle - cycles_per_img
end
-- add padding if image is too simple
while current_cycle<cycles_per_img do
blk = blk .. string.char(audio:next_sample()*4+2,0,curr[0])
pos = 1
current_cycle = current_cycle + cycles
test_fin_bloc()
end
-- next image
video:next_image()
current_cycle = current_cycle - cycles_per_img
end
test_fin_bloc()
blk = blk .. string.char(3,255,255)
OUT:write(blk .. string.rep(string.char(255),512-blk:len()))
OUT:close()
audio:close()
video:close()
io.stdout:write('\n')
io.stdout:flush()
J'ai testé ce filtre vidéo avec des clips bien pêchus principalement à base de bon gros rock qui tâche (mais pas que ca) et je trouve que ca ressort très bien au final . En outre question audio ca me permet aussi d’entendre que les aigus passent bien en dépit des pauvre 5khz du player.
Notes:
(1) les liens sont temporaires (durée 1 mois environ)
(2) vidéos avec un filtre temporal différent ainsi qu'un niveau de "dither" supplémentaire permettant de voir un peu plus de détails sombres (particulièrement dans le clip "Thriller"): >>ici<<
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
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Re: [Thomson] SDDRIVE Vidéo
Oui, c'est de mieux en mieux. Mais pour ce type de vidéo, il faudrait beaucoup plus de pixels pour convaincre le spectateur moyen.
Malheureusement les pixels sont plus faciles à voir que la performance technique
Je note une fois de plus que ça passe mieux sur un moniteur Thomson que sur un écran moderne, à cause du gamma, de la luminosité et de la couleur de fond moins noire.
Malheureusement les pixels sont plus faciles à voir que la performance technique
Je note une fois de plus que ça passe mieux sur un moniteur Thomson que sur un écran moderne, à cause du gamma, de la luminosité et de la couleur de fond moins noire.
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.
-
- Messages : 7987
- Inscription : 18 sept. 2010 12:08
- Localisation : Brest et parfois les Flandres
Re: [Thomson] SDDRIVE Vidéo
C'est sur que techniquement peu de personnes ne réalisent que le rafraichissement complet de l'écran sur thomson est d'une lenteur absolue. Un simple CLS à l'écran s'écrit
Code : Tout sélectionner
F070 3636 PSHU Y,X,B,A 11
F072 3630 PSHU Y,X 9
F074 119360 CMPU <$60 7
F077 2EF7 BGT $F070 3
Si on avait un mode texte qui permette de remplir 8 octets vidéos en écrivant un seul octet en mémoire, on pourrait peut-être faire mieux (cf les streams sur C64). Mais étant donné qu'on a juste un mode graphique, écrire ne serait-ce qu'un caractère de 8 octets coute un beau:
Code : Tout sélectionner
LDX #TABCHAR (3)
CLRA (2)
LSLB (2)
ROLA (2)
LSLB (2)
ROLA (2)
LSLB (2)
ROLA (2)
LEAX D,X (8)
LDB ,X (4)
STB ,Y (4)
LDB 1,X (5)
STB 40,Y (5)
LDB 2,X (5)
STB 80,Y (5)
LDB 3,X (5)
STB 120,Y (5)
LDB 4,X (5)
STB 160,Y (6)
LDB 5,X (5)
STB 200,Y (6)
LDB 6,X (5)
STB 240,Y (6)
LDB 7,X (5)
STB 280,Y (6)
Bref, j'ai pas trop d'idée pour tirer mieux du débit de la carte SD ni même du débit du pauvre 6809 qui utilise quand même 11 cycles pour recopier un octet en mémoire ce qui le limite intrinsèquement à faire du 11fps maxi en haute résolution quand il faut changer tout l'écran. Je pense que le player ASM actuel et le mode 80x50 est sans doute ce qu'on peut faire de mieux.
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
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
-
- Messages : 7987
- Inscription : 18 sept. 2010 12:08
- Localisation : Brest et parfois les Flandres
Re: [Thomson] SDDRIVE Vidéo
Quand on regarde le code du player, on voit qu'en fin de bloc on a plusieurs attentes de 147 et 190 cycles pendant lesquels on ne fait rien. Or en desassemblant les différentes ROM thomson de DCMOTO je me rends compte que la routine KTST (détection d'appui sur une touche) prends respectivement:
Ou constate donc que même avec l'infâme routine du TO7/70 on tient largement dans les temps d'attente de fin de bloc. Il doit donc être possible de detecter l'appui sur une touche "gratuitement" (mais au prix d'une certaine complexité dans le code car il faut caler ces routines sur une durée homogène). Si une touche est appuyée alors le player peut appeller GETC (récupération de la touche appuyée) qui est plus long, mais c'est pas grave car c'est là qu'on peut faire des trucs sympa en plus du player:
________
(1) comme le player joue 173 échantillons audio par bloc, un bloc est lu en 173*199=34427µs et 10s représentent un saut de 290 blocs consécutifs. Il est donc relativement facile de sauter 10s de stream: on appelle la routine SAUT 290 fois, puis on lit le(s) bloc(s) suivant jusqu'à trouver le code "saut à une adresse en ram vidéo" (code approximatif)
(2) si on n'affiche plus la partie vidéo le player peut lire une trame de 3 octets comme suit: soit 112cycles. Cela permet de streamer le son au max à 8.9khz soit 77% plus vite au mieux. Quand on a réalisé ca, on comprends que la partie vidéo est pas mal optimisée tout compte fait car elle ne tue pas trop la vitesse maxi qu'on peut tirer du streaming bit par bit de la carte SD.
- 25 cycles sur TO9, TO9DE, TO8 proto #14
- 32 cycles sur TO7
- 41 cycles sur TO8, TO9+, T9000
- 181 cycles sur TO7/70
Ou constate donc que même avec l'infâme routine du TO7/70 on tient largement dans les temps d'attente de fin de bloc. Il doit donc être possible de detecter l'appui sur une touche "gratuitement" (mais au prix d'une certaine complexité dans le code car il faut caler ces routines sur une durée homogène). Si une touche est appuyée alors le player peut appeller GETC (récupération de la touche appuyée) qui est plus long, mais c'est pas grave car c'est là qu'on peut faire des trucs sympa en plus du player:
- Touche "STOP" --> pause du player
- Touche "ACC" --> sortie du player
- Touche "->" --> avancée de 10s dans le stream (1)
- Touche "2" --> joue le stream ~2x plus vite (vidéo éteinte) (2)
- Touche "1" --> retour au stream 1x (vidéo allumée)
________
(1) comme le player joue 173 échantillons audio par bloc, un bloc est lu en 173*199=34427µs et 10s représentent un saut de 290 blocs consécutifs. Il est donc relativement facile de sauter 10s de stream: on appelle la routine SAUT 290 fois, puis on lit le(s) bloc(s) suivant jusqu'à trouver le code "saut à une adresse en ram vidéo"
Code : Tout sélectionner
REPT 6,GET_BIT saut échantillon audio
GET_BIT carry=1 si trame position
BCS TRAME_POS
REP 17,GET_BIT
RTS
(2) si on n'affiche plus la partie vidéo le player peut lire une trame de 3 octets comme suit:
Code : Tout sélectionner
REPT 6,READ_BIT lecture donnee son (36)
STB <$CD joue echantillon (4)
REPT 18,GET_BIT saute 18bits (72)
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
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Re: [Thomson] SDDRIVE Vidéo
J'avais déjà pensé à cette amélioration possible. S'il n'y avait que le MO5 ce serait facile, on peut tester l'appui sur une touche en 10 cycles (sans appeler KTST). Avec les autres machines c'est beaucoup plus compliqué, il est difficile de faire mieux que la routine KTST, et le nombre de cycles est variable d'une machine à l'autre. Dans dcmoto c'est encore différent, car le clavier n'est pas émulé au niveau matériel.
Sur MO5, l'adresse de KTST est en $F0CF. Normalement cette adresse devrait être $F1D2.
Le nombre de cycles est variable (et grand), car les touches sont testées une par une jusqu'à la première enfoncée.
Par contre si on teste une touche particulière il est inutile de faire une boucle, il ne faut pas passer par KTST.
Sur MO5, l'adresse de KTST est en $F0CF. Normalement cette adresse devrait être $F1D2.
Le nombre de cycles est variable (et grand), car les touches sont testées une par une jusqu'à la première enfoncée.
Par contre si on teste une touche particulière il est inutile de faire une boucle, il ne faut pas passer par KTST.
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.
-
- Messages : 7987
- Inscription : 18 sept. 2010 12:08
- Localisation : Brest et parfois les Flandres
Re: [Thomson] SDDRIVE Vidéo
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
A500 Vampire V2+ ^8^, A1200 (030@50mhz/fpu/64mb/cf 8go),
A500 GVP530(MMU/FPU) h.s., R-Pi, TO9, TO8D, TO8.Démos
Re: [Thomson] SDDRIVE Vidéo
Oui, la musique est toujours excellente. Pour la vidéo il faut un peu d'imagination dans les passages sombres.
Finalement je crois que c'est mieux en couleurs, même s'il n'y a pas 20 fps, comme Bad Apple par exemple.
Finalement je crois que c'est mieux en couleurs, même s'il n'y a pas 20 fps, comme Bad Apple par exemple.
Daniel
L'obstacle augmente mon ardeur.
L'obstacle augmente mon ardeur.