Kiel mi reakiris datumojn en nekonata formato de magneta bendo

antaŭhistorio

Estante amanto de retroa aparataro, mi iam aĉetis ZX Spectrum+ de vendisto en Britio. Inkluzive kun la komputilo mem, mi ricevis plurajn sonkasedojn kun ludoj (en la originala pakaĵo kun instrukcioj), kaj ankaŭ programojn registritajn sur kasedoj sen specialaj markoj. Mirinde, datumoj de 40-jaraj kasedoj estis bone legeblaj kaj mi povis elŝuti preskaŭ ĉiujn ludojn kaj programojn el ili.

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Tamen sur kelkaj kasedoj mi trovis registradojn, kiuj klare ne estis faritaj de la komputilo ZX Spectrum. Ili sonis tute alie kaj, male al la registradoj el la menciita komputilo, ili ne komenciĝis per mallonga BASIC-ŝargilo, kiu kutime ĉeestas en la registradoj de ĉiuj programoj kaj ludoj.

Dum kelka tempo tio hantis min – mi tre volis ekscii, kio estas kaŝita en ili. Se vi povus legi la sonsignalon kiel sinsekvon de bajtoj, vi povus serĉi signojn aŭ ion ajn, kiu indikas la originon de la signalo. Ia retro-arkeologio.

Nun kiam mi iris la tutan vojon kaj rigardas la etikedojn de la kasedoj mem, mi ridetas ĉar

la respondo estis ĝuste antaŭ miaj okuloj la tutan tempon
Sur la etikedo de la maldekstra kasedo estas la nomo de la komputilo TRS-80, kaj tuj sub la nomo de la fabrikanto: "Fabrikita de Radio Shack en Usono"

(Se vi volas konservi la intrigon ĝis la fino, ne iru sub la spoiler)

Komparo de sonsignaloj

Antaŭ ĉio, ni ciferecigu la sonregistraĵojn. Vi povas aŭskulti kiel ĝi sonas:


Kaj kiel kutime la registrado de la komputilo ZX Spectrum sonas:


En ambaŭ kazoj, ĉe la komenco de la registrado estas tn pilota tono - sono de la sama frekvenco (en la unua registrado ĝi estas tre mallonga <1 sekundo, sed estas distingebla). La pilottono signalas la komputilon prepariĝi por ricevi datumojn. Kiel regulo, ĉiu komputilo rekonas nur sian "propran" pilottonon per la formo de la signalo kaj ĝia frekvenco.

Necesas diri ion pri la signalformo mem. Ekzemple, sur la ZX Spectrum ĝia formo estas rektangula:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Kiam pilottono estas detektita, la ZX Spectrum montras alternajn ruĝajn kaj bluajn striojn sur la rando de la ekrano por indiki ke la signalo estis rekonita. Pilottono finiĝas sinkrona pulso, kiu signalas al la komputilo komenci ricevi datumojn. Ĝi estas karakterizita per pli mallonga daŭro (kompare kun la pilottono kaj postaj datenoj) (vidu figuron)

Post kiam la sinkroniga pulso estas ricevita, la komputilo registras ĉiun pliiĝon/falon de la signalo, mezurante ĝian daŭron. Se la daŭro estas malpli ol certa limo, bito 1 estas skribita al memoro, alie 0. La bitoj estas kolektitaj en bajtojn kaj la procezo estas ripetita ĝis N bajtoj estas ricevitaj. La nombro N estas kutime prenita de la kaplinio de la elŝutita dosiero. La ŝarĝa sekvenco estas kiel sekvas:

  1. pilota tono
  2. kaplinio (fiksa longo), enhavas la grandecon de la elŝutitaj datumoj (N), dosiernomon kaj tipon
  3. pilota tono
  4. la datumoj mem

Por certigi, ke la datumoj estas ĝuste ŝarĝitaj, la ZX Spectrum legas la tn egaleco bajto (pareca bajto), kiu estas kalkulita dum konservado de dosiero per XORing ĉiuj bajtoj de la skribitaj datumoj. Legante dosieron, la komputilo kalkulas la egalan bajton el la ricevitaj datumoj kaj, se la rezulto diferencas de la konservita, montras la erarmesaĝon "R Tape loading error". Strikte parolante, la komputilo povas elsendi ĉi tiun mesaĝon pli frue se, leginte, ĝi ne povas rekoni pulson (maltrafita aŭ ĝia daŭro ne respondas al certaj limoj)

Do, ni nun vidu kiel aspektas nekonata signalo:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Jen la pilota tono. La formo de la signalo estas signife malsama, sed estas klare, ke la signalo konsistas el ripetado de mallongaj pulsoj de certa frekvenco. Je specimena ofteco de 44100 Hz, la distanco inter la "pintoj" estas proksimume 48 specimenoj (kiu respondas al frekvenco de ~918 Hz). Ni memoru ĉi tiun figuron.

Ni nun rigardu la datumfragmenton:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Se ni mezuras la distancon inter individuaj pulsoj, rezultas, ke la distanco inter "longaj" pulsoj estas ankoraŭ ~48 specimenoj, kaj inter mallongaj - ~24. Rigardante iom antaŭen, mi diros, ke finfine rezultis, ke "referencaj" pulsoj kun ofteco de 918 Hz sekvas senĉese, de la komenco ĝis la fino de la dosiero. Oni povas supozi, ke dum transdonado de datumoj, se aldona pulso estas renkontita inter la referencaj pulsoj, ni konsideras ĝin bito 1, alie 0.

Kio pri la sinkroniga pulso? Ni rigardu la komencon de la datumoj:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

La pilottono finiĝas kaj la datumoj tuj komenciĝas. Iom poste, analizinte plurajn malsamajn sonregistraĵojn, ni povis malkovri, ke la unua bajto de datumoj ĉiam estas la sama (10100101b, A5h). La komputilo povas komenci legi datumojn post kiam ĝi ricevas ĝin.

Vi ankaŭ povas atenti la movon de la unua referenca pulso tuj post la lasta 1-a en la sinkroniga bajto. Ĝi estis malkovrita multe pli poste en la procezo de evoluigado de datuma rekonprogramo, kiam la datumoj komence de la dosiero ne povus esti legitaj stabile.

Nun ni provu priskribi algoritmon, kiu prilaboros sondosieron kaj ŝarĝos datumojn.

Ŝarĝante Datumojn

Unue, ni rigardu kelkajn supozojn por konservi la algoritmon simpla:

  1. Ni konsideros nur dosierojn en formato WAV;
  2. La sondosiero devas komenciĝi per pilota tono kaj ne devas enhavi silenton ĉe la komenco
  3. La fontdosiero devas havi specimenan indicon de 44100 Hz. En ĉi tiu kazo, la distanco inter la referencaj pulsoj de 48 specimenoj jam estas determinita kaj ni ne bezonas kalkuli ĝin programe;
  4. La specimena formato povas esti ajna (8/16 bitoj/flotpunkto) - ĉar dum legado ni povas konverti ĝin al la dezirata;
  5. Ni supozas, ke la fontdosiero estas normaligita per amplitudo, kiu devus stabiligi la rezulton;

La legoritmo estos kiel sekvas:

  1. Ni legas la dosieron en memoron, samtempe konvertante la specimenan formaton al 8 bitoj;
  2. Determini la pozicion de la unua pulso en la sondatumoj. Por fari tion, vi devas kalkuli la nombron de la specimeno kun la maksimuma amplitudo. Por simpleco, ni kalkulos ĝin unufoje permane. Ni konservu ĝin al la variablo prev_pos;
  3. Aldonu 48 al la pozicio de la lasta pulso (pos := prev_pos + 48)
  4. Ĉar pliigi la pozicion je 48 ne garantias, ke ni atingos la pozicion de la sekva referenca pulso (benddifektoj, malstabila funkciado de la bendo-veturadmekanismo, ktp.), ni devas ĝustigi la pozicion de la pospulso. Por fari tion, prenu malgrandan datumon (pos-8;pos+8) kaj trovu la maksimuman ampleksan valoron sur ĝi. La pozicio responda al la maksimumo estos konservita en pos. Ĉi tie 8 = 48/6 estas eksperimente akirita konstanto, kiu garantias ke ni determinos la ĝustan maksimumon kaj ne influos aliajn impulsojn kiuj povas esti proksime. En tre malbonaj kazoj, kiam la distanco inter pulsoj estas multe malpli ol aŭ pli granda ol 48, vi povas efektivigi devigitan serĉon de pulso, sed en la medio de la artikolo mi ne priskribos ĉi tion en la algoritmo;
  5. Ĉe la antaŭa paŝo, ankaŭ necesus kontroli, ke la referenca pulso entute troviĝis. Tio estas, se vi simple serĉas la maksimumon, ĉi tio ne garantias, ke la impulso ĉeestas en ĉi tiu segmento. En mia lasta efektivigo de la legoprogramo, mi kontrolas la diferencon inter la maksimumaj kaj minimumaj amplitudaj valoroj sur segmento, kaj se ĝi superas certan limon, mi kalkulas la ĉeeston de impulso. La demando ankaŭ estas kion fari se la referenca pulso ne estas trovita. Estas 2 opcioj: aŭ la datumoj finiĝis kaj silento sekvas, aŭ tio devus esti konsiderata kiel legado-eraro. Tamen, ni preterlasos ĉi tion por simpligi la algoritmon;
  6. Je la sekva paŝo, ni devas determini la ĉeeston de datumpulso (bito 0 aŭ 1), por tio ni prenas la mezon de la segmento (antaŭa_pos;pos) meza_pos egala al meza_pos := (antaŭa_pos+pos)/2 kaj en iu najbareco de meza_pozo sur la segmento (meza_pozo-8;meza_pozo +8) ni kalkulu la maksimuman kaj minimuman amplekson. Se la diferenco inter ili estas pli ol 10, ni skribas biton 1 en la rezulton, alie 0. 10 estas konstanto akirita eksperimente;
  7. Konservu la nunan pozicion en prev_pos (antaŭa_pos := pos)
  8. Ripeti ekde paŝo 3 ĝis ni legas la tutan dosieron;
  9. La rezulta bita tabelo devas esti konservita kiel aro de bajtoj. Ĉar ni ne enkalkulis la sinkronigan bajton dum legado, la nombro da bitoj eble ne estas oblo de 8, kaj la bezonata bita ofseto ankaŭ estas nekonata. En la unua efektivigo de la algoritmo, mi ne sciis pri la ekzisto de la sinkroniga bajto kaj tial simple konservis 8 dosierojn kun malsamaj nombroj da ofsetaj bitoj. Unu el ili enhavis ĝustajn datumojn. En la fina algoritmo, mi simple forigas ĉiujn bitojn ĝis A5h, kio ebligas al mi tuj ricevi la ĝustan eligdosieron

Algoritmo en Ruby, por interesitoj
Mi elektis Ruby kiel la lingvon por verki la programon, ĉar... Mi programas sur ĝi plejofte. La opcio ne estas alt-efika, sed la tasko fari la legan rapidon kiel eble plej rapide ne valoras ĝin.

# Используем 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*")

rezulto

Provinte plurajn variantojn de la algoritmo kaj konstantoj, mi bonŝancis akiri ion ege interesan:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Do, juĝante laŭ la signoĉenoj, ni havas programon por grafikaĵoj. Tamen, ne estas ŝlosilvortoj en la programa teksto. Ĉiuj ŝlosilvortoj estas koditaj kiel bajtoj (ĉiu valoro > 80h). Nun ni devas ekscii, kiu komputilo el la 80-aj jaroj povus konservi programojn en ĉi tiu formato.

Fakte, ĝi estas tre simila al BASIC-programo. La komputilo ZX Spectrum stokas programojn en proksimume la sama formato en memoro kaj konservas programojn sur sonbendon. Ĉiaokaze, mi kontrolis la ŝlosilvortojn kontraŭ tablo. Tamen, la rezulto estis evidente negativa.

Mi ankaŭ kontrolis la BASIC-ŝlosilvortojn de la popularaj Atari, Commodore 64 kaj pluraj aliaj tiamaj komputiloj, pri kiuj mi povis trovi dokumentadon, sed sen sukceso - mia scio pri la specoj de retrokomputiloj montriĝis ne tiom larĝa.

Tiam mi decidis iri la listo, kaj tiam mia rigardo falis sur la nomon de la fabrikanto Radio Shack kaj la komputilo TRS-80. Jen la nomoj, kiuj estis skribitaj sur la etikedoj de la kasedoj, kiuj kuŝis sur mia tablo! Mi ne antaŭe konis tiujn nomojn kaj ne konis la komputilon TRS-80, do ŝajnis al mi, ke Radio Shack estas fabrikanto de sonkasedoj kiel BASF, Sony aŭ TDK, kaj la TRS-80 estis la tempo de reprodukto. Kial ne?

Komputilo Tandy/Radio Shack TRS-80

Estas tre verŝajne, ke la koncerna sonregistraĵo, kiun mi donis kiel ekzemplon komence de la artikolo, estis farita en komputilo tiel:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Montriĝis, ke ĉi tiu komputilo kaj ĝiaj varioj (Modelo I/Modelo III/Modelo IV, ktp.) estis tre popularaj iam (kompreneble, ne en Rusio). Estas rimarkinde, ke la procesoro, kiun ili uzis, estis ankaŭ Z80. Por ĉi tiu komputilo vi povas trovi en la Interreto multe da informoj. En la 80-aj jaroj, komputilaj informoj estis distribuitaj en revuoj. Nuntempe estas pluraj emuliloj komputiloj por malsamaj platformoj.

Mi elŝutis la emulilon trs80gp kaj unuafoje mi povis vidi kiel funkcias ĉi tiu komputilo. Kompreneble, la komputilo ne apogis kolorproduktaĵon; la ekranrezolucio estis nur 128x48 pikseloj, sed ekzistis multaj etendaĵoj kaj modifoj kiuj povis pliigi la ekranrezolucion. Ekzistis ankaŭ multaj ebloj por operaciumoj por ĉi tiu komputilo kaj ebloj por efektivigi la BASIC-lingvon (kiu, male al la ZX Spectrum, en kelkaj modeloj eĉ ne estis "fulmigita" en ROM kaj ajna opcio povus esti ŝargita de disketo, same kiel la OS mem)

Ankaŭ mi trovis utileco konverti sonregistraĵojn al formato CAS, kiu estas subtenata de emuliloj, sed ial ne eblis legi registradojn el miaj kasedoj uzante ilin.

Eltrovinte la CAS-dosierformaton (kiu montriĝis nur iom post iom kopio de la datumoj de la bendo, kiun mi jam havis ĉemane, krom la kaplinio kun ĉeesto de sinkroniga bajto), mi faris malmultaj ŝanĝoj al mia programo kaj povis eligi funkciantan CAS-dosieron kiu funkciis en la emulilo (TRS-80 Model III):

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Mi desegnis la lastan version de la konverta ilo kun aŭtomata determino de la unua pulso kaj la distanco inter referencaj pulsoj kiel GEM-pakaĵo, la fontkodo haveblas ĉe GitHub.

konkludo

La vojo, kiun ni vojaĝis, montriĝis fascina vojaĝo en la pasintecon, kaj mi ĝojas, ke finfine mi trovis la respondon. Interalie mi:

  • Mi eltrovis la formaton por konservi datumojn en la ZX Spectrum kaj studis la enkonstruitajn ROM-rutinojn por konservi/legi datumojn el sonkasedoj.
  • Mi konatiĝis kun la komputilo TRS-80 kaj ĝiaj varioj, studis la operaciumon, rigardis ekzemplajn programojn kaj eĉ havis la ŝancon fari sencimigojn en maŝinkodoj (finfine, ĉiuj Z80-mnemonikoj estas konataj al mi)
  • Verkis plenrajtan ilon por konverti sonregistraĵojn al CAS-formato, kiu povas legi datumojn, kiuj ne estas rekonitaj de la "oficiala" ilo.

fonto: www.habr.com

Aldoni komenton