historiaurrea
Retro hardwarearen maitalea izaki, behin ZX Spectrum+ bat erosi nuen Erresuma Batuko saltzaile bati. Ordenagailuarekin batera sartuta, hainbat audio-kasete jaso nituen jokoekin (jatorrizko ontzian argibideekin), baita marka berezirik gabeko kaseteetan grabatutako programak ere. Harrigarria bada ere, 40 urteko kaseteetako datuak ondo irakurtzen ziren eta haietatik ia joko eta programa guztiak deskargatu ahal izan nituen.
Hala ere, kasete batzuetan ZX Spectrum ordenagailuak egin gabeko grabazioak aurkitu ditut. Soinu guztiz ezberdina zuten eta, aipatutako ordenagailuko grabazioek ez bezala, ez ziren BASIC abiarazle labur batekin hasi, programa eta joko guztien grabazioetan egon ohi dena.
Aspaldi batez, honek hondatu ninduen - benetan jakin nahi nuen haietan zer zegoen ezkutatuta. Audio-seinalea byte-sekuentzia gisa irakurriko bazenu, karaktereak edo seinalearen jatorria adierazten duen edozer bilatu dezakezu. Retro-arkeologia moduko bat.
Orain bide osoa egin eta kaseten etiketak beraiek begiratuta, irribarre egiten dut zeren eta
erantzuna nire begien aurrean egon zen denbora guztian
Ezkerreko kasetearen etiketan TRS-80 ordenagailuaren izena dago, eta fabrikatzailearen izenaren azpian: "Radio Shack-ek AEBetan fabrikatua"
(Intriga amaierara arte mantendu nahi baduzu, ez sartu spoiler azpian)
Audio-seinaleen alderaketa
Lehenik eta behin, digitaliza ditzagun audio-grabaketak. Nolako soinua entzun dezakezu:
Eta ohi bezala ZX Spectrum ordenagailutik egindako grabaketak soinuak ematen ditu:
Bi kasuetan, grabazioaren hasieran deitzen den bat dago tonu pilotua - Maiztasun bereko soinua (lehenengo grabazioan oso laburra da <1 segundo, baina bereizgarria da). Pilotu-tonuak ordenagailuari seinalea ematen dio datuak jasotzeko prestatzeko. Oro har, ordenagailu bakoitzak bere tonu pilotu "propia" bakarrik ezagutzen du seinalearen formaren eta maiztasunaren arabera.
Seinalearen forma berari buruz zerbait esan beharra dago. Adibidez, ZX Spectrum-en bere forma laukizuzena da:
Pilotu-tonu bat hautematen denean, ZX Spectrum-ek barra gorriak eta urdinak txandakatu egiten ditu pantailaren ertzean, seinalea ezagutu dela adierazteko. Tonu pilotua amaitzen da pultsu sinkronikoa, ordenagailuari datuak jasotzen hasteko seinalea ematen diona. Iraupen laburragoa da (pilotu tonuarekin eta ondorengo datuekin alderatuta) (ikus irudia)
Sinkronizazio-pultsua jaso ondoren, ordenagailuak seinalearen igoera/jaitsiera bakoitza erregistratzen du, bere iraupena neurtuz. Iraupena muga jakin bat baino txikiagoa bada, 1 bit idazten da memorian, bestela 0. Bitak bytetan biltzen dira eta prozesua errepikatzen da N byte jaso arte. N zenbakia deskargatutako fitxategiaren goiburutik hartu ohi da. Kargatzeko sekuentzia hau da:
- tonu pilotua
- goiburua (luzera finkoa), deskargatutako datuen tamaina (N), fitxategiaren izena eta mota ditu
- tonu pilotua
- datuak berak
Datuak behar bezala kargatzen direla ziurtatzeko, ZX Spectrum-ek deitutakoa irakurtzen du parekotasun byte (paritate-byte), fitxategi bat gordetzean kalkulatzen dena idatzitako datuen byte guztiak XOR eginez. Fitxategi bat irakurtzean, ordenagailuak paritate-bytea kalkulatzen du jasotako datuetatik eta, emaitza gordetakoarekin alderatuta, errore-mezua bistaratzen du "R Tape loading error". Zorrotz esanda, ordenagailuak mezu hau lehenago igorri dezake, irakurtzean, pultsurik ezagutzen ez badu (galdutako edo haren iraupena muga batzuekin bat ez badator)
Beraz, ikus dezagun seinale ezezagun bat nolakoa den:
Hau da tonu pilotua. Seinalearen forma nabarmen desberdina da, baina argi dago seinalea maiztasun jakin bateko pultsu laburrak errepikatzean datza. 44100 Hz-ko laginketa-maiztasunarekin, "tontorren" arteko distantzia gutxi gorabehera 48 lagin da (~ 918 Hz-eko maiztasunari dagokio).Gogora dezagun irudi hau.
Ikus dezagun orain datu zatia:
Pultsu indibidualen arteko distantzia neurtzen badugu, ikusten da pultsu "luzeen" arteko distantzia ~ 48 lagin dela oraindik, eta laburren artean - ~ 24. Aurrera begira, esango dut azkenean 918 Hz-ko maiztasuneko βerreferentziaβ pultsuak etengabe jarraitzen dutela, fitxategiaren hasieratik amaierara arte. Onar daiteke datuak igortzerakoan, erreferentzia-pultsuen artean pultsu gehigarri bat aurkitzen bada, 1 bittzat hartuko dugula, bestela 0.
Zer gertatzen da sinkronizazio-pultsuarekin? Ikus ditzagun datuen hasiera:
Tonu pilotua amaitzen da eta datuak berehala hasten dira. Pixka bat beranduago, hainbat audio-grabaketa aztertu ondoren, datuen lehen bytea beti berdina dela deskubritu ahal izan genuen (10100101b, A5h). Baliteke ordenagailua datuak irakurtzen hastea jaso ondoren.
Lehen erreferentzia-pultsuaren desplazamenduari ere errepara diezaiokezu sinkronizazio bytearen azken 1.aren ondoren. Askoz beranduago aurkitu zen datuak ezagutzeko programa garatzeko prozesuan, fitxategiaren hasierako datuak modu egonkorrean irakurri ezin zirenean.
Orain saia gaitezen audio fitxategi bat prozesatu eta datuak kargatuko dituen algoritmo bat deskribatzen.
Datuak kargatzen
Lehenik eta behin, ikus ditzagun hipotesi batzuk algoritmoa sinplea mantentzeko:
- WAV formatuan dauden fitxategiak soilik hartuko ditugu kontuan;
- Audio-fitxategiak tonu pilotu batekin hasi behar du eta ez du isiltasunik izan behar hasieran
- Iturburu-fitxategiak 44100 Hz-ko laginketa-abiadura izan behar du. Kasu honetan, 48 laginen erreferentzia-pultsuen arteko distantzia zehaztuta dago jada eta ez dugu programatikoki kalkulatu beharrik;
- Lagin-formatua edozein izan daiteke (8/16 bit/kopuru mugikorra) - irakurtzerakoan nahi den moduan bihur dezakegulako;
- Suposatzen dugu iturburu-fitxategia anplitudearen arabera normalizatuta dagoela, eta horrek emaitza egonkortu beharko luke;
Irakurketa algoritmoa honako hau izango da:
- Fitxategia memorian irakurtzen dugu, aldi berean lagin formatua 8 bitera bihurtuz;
- Zehaztu lehen pultsuaren posizioa audio-datuetan. Horretarako, laginaren kopurua kalkulatu behar duzu anplitude maximoarekin. Sinpletasuna lortzeko, behin eskuz kalkulatuko dugu. Gorde dezagun prev_pos aldagaian;
- Gehitu 48 azken pultsuaren posizioari (pos := prev_pos + 48)
- Posizioa 48z handitzeak hurrengo erreferentzia-pultsuaren posiziora iritsiko garela bermatzen ez duenez (zintaren akatsak, zinta gidatzeko mekanismoaren funtzionamendu ezegonkorra, etab.), pos pultsuaren posizioa egokitu behar dugu. Horretarako, hartu datu txiki bat (pos-8;pos+8) eta aurkitu bertan anplitudearen balio maximoa. Gehienezkoari dagokion posizioa pos. Hemen 8 = 48/6 esperimentalki lortutako konstante bat da, eta horrek bermatzen du maximo zuzena zehaztuko dugula eta ez duela eragingo hurbil egon daitezkeen beste bulkadarik. Oso kasu txarretan, pultsuen arteko distantzia 48 baino askoz txikiagoa edo handiagoa denean, pultsu baten bilaketa behartua ezar dezakezu, baina artikuluaren esparruan ez dut hori algoritmoan deskribatuko;
- Aurreko urratsean, erreferentziazko pultsu bat aurkitu dela egiaztatu beharko litzateke. Hau da, maximoa besterik gabe bilatzen baduzu, horrek ez du bermatzen bulkada segmentu honetan dagoenik. Irakurketa programaren azken inplementazioan, segmentu batean anplitude maximoaren eta gutxieneko balioen arteko aldea egiaztatzen dut, eta muga jakin bat gainditzen badu, bulkada baten presentzia zenbatzen dut. Galdera ere bada zer egin erreferentzia-pultsua aurkitzen ez bada. 2 aukera daude: edo datuak amaitu eta isiltasuna jarraitzen du, edo hau irakurketa-erroretzat hartu behar da. Hala ere, hori baztertuko dugu algoritmoa sinplifikatzeko;
- Hurrengo urratsean, datu-pultsu baten presentzia zehaztu behar dugu (0 edo 1 bit), horretarako segmentuaren erdia hartuko dugu (prev_pos;pos) middle_pos erdiko_pos berdina := (prev_pos+pos)/2 eta segmentuko erdiko_pos-en auzo batzuetan (erdiko_pos-8;erdiko_pos +8) kalkula ditzagun anplitude maximoa eta minimoa. Bien arteko aldea 10 baino handiagoa bada, emaitzan 1 bit idatziko dugu, bestela 0. 10 esperimentalki lortutako konstantea da;
- Gorde uneko posizioa prev_pos-en (prev_pos := pos)
- Errepikatu 3. urratsetik hasita fitxategi osoa irakurri arte;
- Sortutako bit-matrizea byte multzo gisa gorde behar da. Irakurtzerakoan sinkronizazio-bytea kontuan hartu ez dugunez, baliteke bit kopurua ez izatea 8ren multiploa, eta behar den bit-desplazamendua ere ezezaguna da. Algoritmoaren lehen inplementazioan, ez nekien sinkronizazio-bytearen existentziari buruz eta, beraz, 8 fitxategi gorde besterik ez nuen desplazamendu-bit kopuru desberdinekin. Horietako batek datu zuzenak zituen. Azken algoritmoan, A5h-ra arteko bit guztiak kentzen ditut, eta horrek irteerako fitxategi zuzena berehala lortzeko aukera ematen dit
Ruby-n algoritmoa, interesa dutenentzat
Ruby aukeratu nuen programa idazteko hizkuntza gisa, zeren... Gehienetan bertan programatzen dut. Aukera ez da errendimendu handikoa, baina irakurketa abiadura ahalik eta azkarren egiteko lanak ez du merezi.
# ΠΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ gem 'wavefile'
require 'wavefile'
reader = WaveFile::Reader.new('input.wav')
samples = []
format = WaveFile::Format.new(:mono, :pcm_8, 44100)
# Π§ΠΈΡΠ°Π΅ΠΌ WAV ΡΠ°ΠΉΠ», ΠΊΠΎΠ½Π²Π΅ΡΡΠΈΡΡΠ΅ΠΌ Π² ΡΠΎΡΠΌΠ°Ρ Mono, 8 bit
# ΠΠ°ΡΡΠΈΠ² samples Π±ΡΠ΄Π΅Ρ ΡΠΎΡΡΠΎΡΡΡ ΠΈΠ· Π±Π°ΠΉΡ ΡΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΡΠΌΠΈ 0-255
reader.each_buffer(10000) do |buffer|
samples += buffer.convert(format).samples
end
# ΠΠΎΠ·ΠΈΡΠΈΡ ΠΏΠ΅ΡΠ²ΠΎΠ³ΠΎ ΠΈΠΌΠΏΡΠ»ΡΡΠ° (Π²ΠΌΠ΅ΡΡΠΎ 0)
prev_pos = 0
# Π Π°ΡΡΡΠΎΡΠ½ΠΈΠ΅ ΠΌΠ΅ΠΆΠ΄Ρ ΠΈΠΌΠΏΡΠ»ΡΡΠ°ΠΌΠΈ
distance = 48
# ΠΠ½Π°ΡΠ΅Π½ΠΈΠ΅ ΡΠ°ΡΡΡΠΎΡΠ½ΠΈΡ Π΄Π»Ρ ΠΎΠΊΡΠ΅ΡΡΠ½ΠΎΡΡΠΈ ΠΏΠΎΠΈΡΠΊΠ° Π»ΠΎΠΊΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΌΠ°ΠΊΡΠΈΠΌΡΠΌΠ°
delta = (distance / 6).floor
# ΠΠΈΡΡ Π±ΡΠ΄Π΅ΠΌ ΡΠΎΡ
ΡΠ°Π½ΡΡΡ Π² Π²ΠΈΠ΄Π΅ ΡΡΡΠΎΠΊΠΈ ΠΈΠ· "0" ΠΈ "1"
bits = ""
loop do
# Π Π°ΡΡΡΠΈΡΡΠ²Π°Π΅ΠΌ ΠΏΠΎΠ·ΠΈΡΠΈΡ ΡΠ»Π΅Π΄ΡΡΡΠ΅Π³ΠΎ ΠΈΠΌΠΏΡΠ»ΡΡΠ°
pos = prev_pos + distance
# ΠΡΡ
ΠΎΠ΄ΠΈΠΌ ΠΈΠ· ΡΠΈΠΊΠ»Π° Π΅ΡΠ»ΠΈ Π΄Π°Π½Π½ΡΠ΅ Π·Π°ΠΊΠΎΠ½ΡΠΈΠ»ΠΈΡΡ
break if pos + delta >= samples.size
# ΠΠΎΡΡΠ΅ΠΊΡΠΈΡΡΠ΅ΠΌ ΠΏΠΎΠ·ΠΈΡΠΈΡ pos ΠΎΠ±Π½Π°ΡΡΠΆΠ΅Π½ΠΈΠ΅ΠΌ ΠΌΠ°ΠΊΡΠΈΠΌΡΠΌΠ° Π½Π° ΠΎΡΡΠ΅Π·ΠΊΠ΅ [pos - delta;pos + delta]
(pos - delta..pos + delta).each { |p| pos = p if samples[p] > samples[pos] }
# ΠΠ°Ρ
ΠΎΠ΄ΠΈΠΌ ΡΠ΅ΡΠ΅Π΄ΠΈΠ½Ρ ΠΎΡΡΠ΅Π·ΠΊΠ° [prev_pos;pos]
middle_pos = ((prev_pos + pos) / 2).floor
# ΠΠ΅ΡΠ΅ΠΌ ΠΎΠΊΡΠ΅ΡΡΠ½ΠΎΡΡΡ Π² ΡΠ΅ΡΠ΅Π΄ΠΈΠ½Π΅
sample = samples[middle_pos - delta..middle_pos + delta]
# ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ Π±ΠΈΡ ΠΊΠ°ΠΊ "1" Π΅ΡΠ»ΠΈ ΡΠ°Π·Π½ΠΈΡΠ° ΠΌΠ΅ΠΆΠ΄Ρ ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡΠ½ΡΠΌ ΠΈ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΡΠΌ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ΠΌ Π½Π° ΠΎΡΡΠ΅Π·ΠΊΠ΅ ΠΏΡΠ΅Π²ΡΡΠ°Π΅Ρ 10
bit = sample.max - sample.min > 10
bits += bit ? "1" : "0"
end
# ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΡΠΈΠ½Ρ
ΡΠΎ-Π±Π°ΠΉΡ ΠΈ Π·Π°ΠΌΠ΅Π½ΡΠ΅ΠΌ Π²ΡΠ΅ ΠΏΡΠ΅Π΄ΡΠ΅ΡΡΠ²ΡΡΡΠΈΠ΅ Π±ΠΈΡΡ Π½Π° 256 Π±ΠΈΡ Π½ΡΠ»Π΅ΠΉ (ΡΠΎΠ³Π»Π°ΡΠ½ΠΎ ΡΠΏΠ΅ΡΠΈΡΠΈΠΊΠ°ΡΠΈΠΈ ΡΠΎΡΠΌΠ°ΡΠ°)
bits.gsub! /^[01]*?10100101/, ("0" * 256) + "10100101"
# Π‘ΠΎΡ
ΡΠ°Π½ΡΠ΅ΠΌ Π²ΡΡ
ΠΎΠ΄Π½ΠΎΠΉ ΡΠ°ΠΉΠ», ΡΠΏΠ°ΠΊΠΎΠ²ΡΠ²Π°Ρ Π±ΠΈΡΡ Π² Π±Π°ΠΉΡΡ
File.write "output.cas", [bits].pack("B*")
Emaitza
Algoritmoaren eta konstanteen hainbat aldaera probatu ondoren, zortea izan nuen zerbait oso interesgarria lortzeko:
Beraz, karaktere-kateak ikusita, grafikoak marrazteko programa dugu. Hala ere, ez dago gako-hitzik programaren testuan. Gako-hitz guztiak byte gisa kodetzen dira (balio bakoitza > 80h). Orain jakin behar dugu 80ko hamarkadako zein ordenagailuk gorde ditzakeen programak formatu honetan.
Izan ere, BASIC programa baten oso antzekoa da. ZX Spectrum ordenagailuak gutxi gorabehera formatu bereko programak gordetzen ditu memorian eta programak zinta batean gordetzen ditu. Badaezpada, gako-hitzak egiaztatu ditut
Garai hartako Atari, Commodore 64 eta beste hainbat ordenagailu ezagunen OINARRIZKO gako-hitzak ere egiaztatu nituen, eta horretarako dokumentazioa aurkitu ahal izan nuen, baina arrakastarik gabe - retro ordenagailu motei buruzko nire ezagutza ez zen hain zabala izan.
Orduan joatea erabaki nuen
Computer Tandy/Radio Shack TRS-80
Oso litekeena da aipaturiko audio-grabazioa, artikulu hasieran adibide gisa jarri dudana, honelako ordenagailu batean egin izana:
Konputagailu hau eta bere barietateak (I eredua / III eredua / IV eredua, etab.) oso ezagunak zirela garai batean (noski, ez Errusian). Aipatzekoa da erabili zuten prozesadorea Z80 ere izan zela. Ordenagailu honetarako Interneten aurki dezakezu
Emuladorea deskargatu dut
ere aurkitu dut
CAS fitxategiaren formatua irudikatuta (lehendik eskuan nuen zintaren datuen kopia bat besterik ez zen izan, sinkronizazio byte bat zegoen goiburukoa izan ezik), bat egin nuen. aldaketa gutxi nire programan eta emuladorean funtzionatzen zuen CAS fitxategi bat atera ahal izan nuen (TRS-80 Model III):
Bihurketa-erabilgarritasunaren azken bertsioa diseinatu nuen lehenengo pultsuaren eta erreferentzia-pultsuen arteko distantziaren determinazio automatikoarekin GEM pakete gisa, iturburu-kodea eskuragarri dago hemen.
Ondorioa
Egin dugun bidea iraganera bidaia liluragarria izan da, eta pozten naiz azkenean erantzuna aurkitu dudalako. Besteak beste, nik:
- ZX Spectrum-en datuak gordetzeko formatua asmatu nuen eta audio kaseteetako datuak gordetzeko/irakurtzeko ROM errutinak aztertu nituen.
- TRS-80 ordenagailua eta bere barietateak ezagutu, sistema eragilea aztertu, lagin-programak aztertu eta makina-kodeetan arazketa egiteko aukera ere izan nuen (azken finean, Z80 mnemoteknia guztiak ezagunak zaizkit)
- Audio-grabaketak CAS formatura bihurtzeko erabilgarritasun oso bat idatzi du, erabilgarritasun "ofizialak" ezagutzen ez dituen datuak irakur ditzakeena.
Iturria: www.habr.com