Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

նախապատմությանը

Լինելով ռետրո սարքավորումների սիրահար՝ ես մի անգամ գնեցի ZX Spectrum+ մի վաճառողից Մեծ Բրիտանիայում: Համակարգչի հետ միասին ես ստացա մի քանի աուդիո ձայներիզներ խաղերով (բնօրինակ փաթեթավորմամբ՝ հրահանգներով), ինչպես նաև ձայներիզների վրա ձայնագրված ծրագրեր՝ առանց հատուկ գծանշումների: Զարմանալիորեն, 40-ամյա ձայներիզների տվյալները լավ ընթեռնելի էին, և ես կարողացա դրանցից ներբեռնել գրեթե բոլոր խաղերն ու ծրագրերը։

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Այնուամենայնիվ, որոշ ձայներիզների վրա ես գտա ձայնագրություններ, որոնք ակնհայտորեն չեն արվել ZX Spectrum համակարգչի կողմից: Դրանք բոլորովին այլ կերպ էին հնչում և, ի տարբերություն նշված համակարգչից կատարվող ձայնագրությունների, չէին սկսում կարճ BASIC բեռնախցիկով, որը սովորաբար առկա է բոլոր ծրագրերի և խաղերի ձայնագրություններում։

Որոշ ժամանակ դա հետապնդում էր ինձ, ես իսկապես ուզում էի պարզել, թե ինչ է թաքնված նրանց մեջ: Եթե ​​կարողանայիք ձայնային ազդանշանը կարդալ որպես բայթերի հաջորդականություն, կարող եք փնտրել նիշեր կամ որևէ բան, որը ցույց է տալիս ազդանշանի ծագումը: Մի տեսակ ռետրո-հնաբանություն.

Հիմա, երբ ես ամբողջ ճանապարհն անցել եմ և նայում եմ ձայներիզների պիտակներին, ես ժպտում եմ, որովհետև.

պատասխանն իմ աչքի առաջ էր ամբողջ ընթացքում
Ձախ կասետի պիտակի վրա TRS-80 համակարգչի անվանումն է, իսկ արտադրողի անվան տակ՝ «Արտադրված է ԱՄՆ-ում Radio Shack-ի կողմից»

(Եթե ուզում եք ինտրիգը պահել մինչև վերջ, մի անցեք սփոյլերի տակ)

Աուդիո ազդանշանների համեմատություն

Նախ թվայնացնենք ձայնագրությունները։ Դուք կարող եք լսել, թե ինչպես է այն հնչում.


Եվ ինչպես միշտ ձայնագրությունը ZX Spectrum համակարգչից հնչում է.


Երկու դեպքում էլ ձայնագրության սկզբում կա այսպես կոչված օդաչուի տոնը - նույն հաճախականության ձայն (առաջին ձայնագրության մեջ այն շատ կարճ է <1 վայրկյան, բայց տարբերվում է): Օդաչուի ազդանշանը համակարգչին ազդանշան է տալիս՝ պատրաստվելու տվյալներ ստանալուն: Որպես կանոն, յուրաքանչյուր համակարգիչ ճանաչում է միայն իր «սեփական» օդաչուի ազդանշանը ազդանշանի ձևով և հաճախականությամբ:

Պետք է ինչ-որ բան ասել ինքնին ազդանշանի ձևի մասին։ Օրինակ, ZX Spectrum-ում դրա ձևը ուղղանկյուն է.

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Երբ հայտնաբերվում է փորձնական ազդանշան, ZX Spectrum-ը ցուցադրում է փոփոխվող կարմիր և կապույտ գծեր էկրանի եզրագծին՝ ցույց տալու, որ ազդանշանը ճանաչվել է: Օդաչուի տոնն ավարտվում է սինխրո զարկերակ, որն ազդանշան է տալիս համակարգչին՝ սկսելու տվյալներ ստանալ: Այն բնութագրվում է ավելի կարճ տևողությամբ (համեմատած փորձնական տոնի և հետագա տվյալների հետ) (տես նկարը)

Համաժամացման իմպուլսը ստանալուց հետո համակարգիչը գրանցում է ազդանշանի յուրաքանչյուր բարձրացում/անկում՝ չափելով դրա տևողությունը: Եթե ​​տեւողությունը որոշակի սահմանից փոքր է, ապա 1-ին բիթը գրվում է հիշողության մեջ, հակառակ դեպքում՝ 0: Բիթերը հավաքվում են բայթերի և գործընթացը կրկնվում է մինչև N բայթ ստանալը: N թիվը սովորաբար վերցվում է ներբեռնված ֆայլի վերնագրից։ Բեռնման հաջորդականությունը հետևյալն է.

  1. օդաչուի տոնը
  2. վերնագիր (ֆիքսված երկարություն), պարունակում է ներբեռնված տվյալների չափը (N), ֆայլի անվանումը և տեսակը
  3. օդաչուի տոնը
  4. տվյալներն ինքնին

Համոզվելու համար, որ տվյալները ճիշտ են բեռնված, ZX Spectrum-ը կարդում է այսպես կոչված հավասարաչափ բայթ (parity byte), որը հաշվարկվում է ֆայլը պահելու ժամանակ՝ XOR-ով գրված տվյալների բոլոր բայթերը: Ֆայլը կարդալիս համակարգիչը ստացված տվյալներից հաշվարկում է պարիտետային բայթը և, եթե արդյունքը տարբերվում է պահպանվածից, ցուցադրում է «R Tape loading error» սխալի հաղորդագրությունը։ Խստորեն ասած, համակարգիչը կարող է ավելի վաղ տալ այս հաղորդագրությունը, եթե կարդալիս այն չի կարող ճանաչել զարկերակը (բաց թողնված կամ դրա տևողությունը չի համապատասխանում որոշակի սահմանների)

Այսպիսով, եկեք տեսնենք, թե ինչ տեսք ունի անհայտ ազդանշանը.

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Սա օդաչուի տոնն է: Ազդանշանի ձևը զգալիորեն տարբերվում է, բայց պարզ է, որ ազդանշանը բաղկացած է որոշակի հաճախականության կարճ իմպուլսների կրկնությունից։ 44100 Հց նմուշառման հաճախականության դեպքում «գագաթների» միջև հեռավորությունը մոտավորապես 48 նմուշ է (որը համապատասխանում է ~918 Հց հաճախականությանը): Հիշենք այս թիվը:

Այժմ նայենք տվյալների հատվածին.

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Եթե ​​չափենք առանձին իմպուլսների միջև հեռավորությունը, ապա կստացվի, որ «երկար» իմպուլսների միջև հեռավորությունը դեռ ~48 նմուշ է, իսկ կարճերինը՝ ~24: Մի փոքր առաջ նայելով՝ կասեմ, որ ի վերջո պարզվեց, որ 918 Հց հաճախականությամբ «տեղեկանք» իմպուլսները հետևում են անընդհատ՝ ֆայլի սկզբից մինչև վերջ։ Կարելի է ենթադրել, որ տվյալներ փոխանցելիս, եթե հղման իմպուլսների միջև հավելյալ զարկերակ է հանդիպում, մենք այն համարում ենք բիթ 1, հակառակ դեպքում՝ 0։

Ինչ վերաբերում է համաժամացման զարկերակին: Եկեք նայենք տվյալների սկզբին.

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Փորձնական տոնն ավարտվում է, և տվյալները անմիջապես սկսվում են: Քիչ անց, մի քանի տարբեր աուդիո ձայնագրություններ վերլուծելուց հետո, մենք կարողացանք պարզել, որ տվյալների առաջին բայթը միշտ նույնն է (10100101b, A5h): Համակարգիչը կարող է սկսել կարդալ տվյալները այն ստանալուց հետո:

Կարող եք նաև ուշադրություն դարձնել առաջին հղման իմպուլսի տեղաշարժին համաժամացման բայթում վերջին 1-ից անմիջապես հետո: Այն հայտնաբերվեց շատ ավելի ուշ՝ տվյալների ճանաչման ծրագրի մշակման գործընթացում, երբ ֆայլի սկզբի տվյալները հնարավոր չէր կայուն կարդալ։

Այժմ փորձենք նկարագրել ալգորիթմ, որը կմշակի աուդիո ֆայլը և կբեռնի տվյալները:

Տվյալների բեռնում

Նախ, եկեք նայենք մի քանի ենթադրությունների՝ ալգորիթմը պարզ պահելու համար.

  1. Մենք կդիտարկենք միայն WAV ձևաչափով ֆայլերը;
  2. Աուդիո ֆայլը պետք է սկսվի փորձնական տոնով և սկզբում չպետք է լռություն պարունակի
  3. Աղբյուրի ֆայլը պետք է ունենա 44100 Հց նմուշառման արագություն: Այս դեպքում 48 նմուշների հղման իմպուլսների միջև հեռավորությունն արդեն որոշված ​​է, և մենք կարիք չունենք այն ծրագրային հաշվարկելու.
  4. Նմուշի ձևաչափը կարող է լինել ցանկացած (8/16 բիթ/լողացող կետ) - քանի որ կարդալիս այն կարող ենք փոխակերպել ցանկալիին.
  5. Մենք ենթադրում ենք, որ աղբյուրի ֆայլը նորմալացված է ամպլիտուդով, որը պետք է կայունացնի արդյունքը.

Ընթերցանության ալգորիթմը կլինի հետևյալը.

  1. Մենք կարդում ենք ֆայլը հիշողության մեջ՝ միևնույն ժամանակ նմուշի ձևաչափը վերածելով 8 բիթ;
  2. Որոշեք առաջին զարկերակի դիրքը աուդիո տվյալների մեջ: Դա անելու համար դուք պետք է հաշվարկեք նմուշի քանակը առավելագույն ամպլիտուդով: Պարզության համար մենք այն մեկ անգամ կհաշվարկենք ձեռքով: Պահպանենք այն prev_pos փոփոխականում;
  3. Վերջին իմպուլսի դիրքին ավելացրեք 48 (pos := prev_pos + 48)
  4. Քանի որ դիրքը 48-ով մեծացնելը չի ​​երաշխավորում, որ մենք կհասնենք հաջորդ հղման զարկերակի դիրքին (ժապավենի թերություններ, ժապավենի շարժիչ մեխանիզմի անկայուն աշխատանք և այլն), մենք պետք է կարգավորենք իմպուլսային pos-ի դիրքը: Դա անելու համար վերցրեք տվյալների փոքր կտոր (pos-8;pos+8) և գտեք դրա վրա առավելագույն ամպլիտուդային արժեքը: Առավելագույնին համապատասխան դիրքը կպահվի pos-ում: Այստեղ 8 = 48/6-ը փորձնականորեն ստացված հաստատուն է, որը երաշխավորում է, որ մենք կորոշենք ճիշտ առավելագույնը և չենք ազդի մյուս իմպուլսների վրա, որոնք կարող են լինել մոտակայքում: Շատ վատ դեպքերում, երբ իմպուլսների միջև հեռավորությունը 48-ից շատ փոքր է կամ մեծ է, կարող է իրականացվել իմպուլսի հարկադիր որոնում, բայց հոդվածի շրջանակներում ես դա չեմ նկարագրի ալգորիթմում.
  5. Նախորդ քայլում անհրաժեշտ կլինի նաև ստուգել, ​​որ հղման զարկերակը ընդհանրապես գտնվել է: Այսինքն, եթե դուք պարզապես փնտրում եք առավելագույնը, դա չի երաշխավորում, որ իմպուլսը առկա է այս հատվածում: Ընթերցանության ծրագրի իմ վերջին կատարման ժամանակ ես ստուգում եմ հատվածի վրա առավելագույն և նվազագույն ամպլիտուդային արժեքների տարբերությունը, և եթե այն գերազանցում է որոշակի սահմանը, ես հաշվում եմ իմպուլսի առկայությունը: Հարցը նաև այն է, թե ինչ անել, եթե հղման զարկերակը չգտնվի: Կա 2 տարբերակ՝ կա՛մ տվյալները ավարտվել են, և՛ լռություն է հետևում, կա՛մ սա պետք է դիտարկել որպես ընթերցման սխալ: Այնուամենայնիվ, մենք բաց կթողնենք սա ալգորիթմը պարզեցնելու համար.
  6. Հաջորդ քայլում մենք պետք է որոշենք տվյալների իմպուլսի առկայությունը (բիթ 0 կամ 1), դրա համար վերցնում ենք հատվածի միջինը (prev_pos;pos) middle_pos հավասար միջին_pos := (prev_pos+pos)/2 և միջին_pos հատվածի որոշ հարևանությամբ (middle_pos-8;middle_pos +8) եկեք հաշվարկենք առավելագույն և նվազագույն ամպլիտուդը: Եթե ​​նրանց միջև տարբերությունը 10-ից ավելի է, արդյունքի մեջ գրում ենք 1 բիթ, հակառակ դեպքում 0. 10-ը փորձնականորեն ստացված հաստատուն է;
  7. Պահպանեք ընթացիկ դիրքը prev_pos-ում (prev_pos := pos)
  8. Կրկնել՝ սկսած 3-րդ քայլից, մինչև կարդանք ամբողջ ֆայլը.
  9. Ստացված բիթային զանգվածը պետք է պահպանվի որպես բայթերի հավաքածու: Քանի որ մենք ընթերցելիս հաշվի չենք առել համաժամացման բայթը, բիթերի թիվը կարող է 8-ի բազմապատիկ չլինել, իսկ բիթերի պահանջվող օֆսեթը նույնպես անհայտ է: Ալգորիթմի առաջին ներդրման ժամանակ ես չգիտեի համաժամացման բայթի գոյության մասին և, հետևաբար, պարզապես պահպանեցի 8 ֆայլ տարբեր թվով օֆսեթ բիթերով: Դրանցից մեկը ճիշտ տվյալներ էր պարունակում։ Վերջնական ալգորիթմում ես պարզապես հեռացնում եմ բոլոր բիթերը մինչև A5h, ինչը թույլ է տալիս անմիջապես ստանալ ճիշտ ելքային ֆայլը

Ալգորիթմ Ruby-ում, հետաքրքրվողների համար
Որպես ծրագիր գրելու լեզու ընտրել եմ Ruby-ը, քանի որ... Ես դրա վրա ծրագրավորում եմ շատ ժամանակ: Տարբերակը բարձր կատարողականություն չունի, բայց ընթերցանության արագությունը հնարավորինս արագ դարձնելու խնդիրը չարժե:

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

Արդյունք

Փորձելով ալգորիթմի և հաստատունների մի քանի տարբերակներ, ես բախտ ունեցա ստանալով չափազանց հետաքրքիր բան.

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Այսպիսով, դատելով նիշերի տողերից, մենք ունենք գրաֆիկներ գծելու ծրագիր: Այնուամենայնիվ, ծրագրի տեքստում հիմնաբառեր չկան: Բոլոր հիմնաբառերը կոդավորված են որպես բայթ (յուրաքանչյուր արժեք > 80ժ): Այժմ մենք պետք է պարզենք, թե 80-ականների ո՞ր համակարգիչը կարող էր այս ձևաչափով ծրագրեր պահել:

Իրականում այն ​​շատ նման է BASIC ծրագրին: ZX Spectrum համակարգիչը մոտավորապես նույն ձևաչափով ծրագրեր է պահում հիշողության մեջ և ծրագրերը պահում ժապավենի վրա: Ամեն դեպքում, ես ստուգեցի հիմնաբառերը սեղան. Սակայն արդյունքն ակնհայտորեն բացասական էր։

Ես ստուգեցի նաև այն ժամանակվա հանրաճանաչ Atari, Commodore 64 և մի քանի այլ համակարգիչների ՀԻՄՆԱԿԱՆ հիմնաբառերը, որոնց համար կարողացա գտնել փաստաթղթեր, բայց անհաջող.

Հետո որոշեցի գնալ ցուցակը, իսկ հետո հայացքս ընկավ արտադրող Radio Shack-ի և TRS-80 համակարգչի անվան վրա։ Սրանք այն անուններն են, որոնք գրված էին իմ սեղանին դրված ձայներիզների պիտակների վրա։ Ես նախկինում չգիտեի այս անունները և ծանոթ չէի TRS-80 համակարգչին, ուստի ինձ թվում էր, որ Radio Shack-ը աուդիո ձայներիզներ արտադրող է, ինչպիսիք են BASF-ը, Sony-ն կամ TDK-ն, և TRS-80-ը նվագարկման ժամանակն էր: Ինչու ոչ?

Համակարգիչ Tandy/Radio Shack TRS-80

Շատ հավանական է, որ խնդրո առարկա աուդիո ձայնագրությունը, որը ես որպես օրինակ բերեցի հոդվածի սկզբում, արված է այսպիսի համակարգչով.

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Պարզվեց, որ այս համակարգիչը և նրա տարատեսակները (Model I/Model III/Model IV և այլն) ժամանակին շատ տարածված էին (իհարկե, ոչ Ռուսաստանում): Հատկանշական է, որ նրանց օգտագործած պրոցեսորը նույնպես Z80 էր։ Այս համակարգչի համար դուք կարող եք գտնել ինտերնետում շատ տեղեկություններ. 80-ականներին համակարգչային տեղեկատվությունը տարածվել է ամսագրեր. Այս պահին կան մի քանիսը էմուլյատորներ համակարգիչներ տարբեր հարթակների համար:

Ես ներբեռնեցի էմուլյատորը trs80gp և առաջին անգամ ես կարողացա տեսնել, թե ինչպես է այս համակարգիչը աշխատում: Իհարկե, համակարգիչը չէր ապահովում գունային ելք, էկրանի լուծաչափը ընդամենը 128x48 պիքսել էր, բայց կային բազմաթիվ ընդլայնումներ և փոփոխություններ, որոնք կարող էին մեծացնել էկրանի լուծաչափը: Կային նաև բազմաթիվ տարբերակներ այս համակարգչի համար օպերացիոն համակարգերի համար և BASIC լեզվի ներդրման տարբերակներ (որը, ի տարբերություն ZX Spectrum-ի, որոշ մոդելներում նույնիսկ չէր «թարթել» ROM-ում, և ցանկացած տարբերակ կարող էր բեռնվել ճկուն սկավառակից, ինչպես ինքնին ՕՀ)

Ես էլ գտա օգտակար աուդիո ձայնագրությունները CAS ձևաչափի փոխարկելու համար, որն ապահովվում է էմուլյատորների կողմից, բայց ինչ-ինչ պատճառներով հնարավոր չի եղել դրանք օգտագործելով իմ ձայներիզներից ձայնագրությունները կարդալ:

Հասկանալով CAS ֆայլի ձևաչափը (որը պարզվեց, որ այն ժապավենի տվյալների մի կտոր առ բիթ պատճենն էր, որը ես արդեն ունեի ձեռքի տակ, բացառությամբ համաժամացման բայթի առկայությամբ վերնագրի), ես պատրաստեցի. մի քանի փոփոխություն իմ ծրագրի մեջ և կարողացա դուրս բերել աշխատանքային CAS ֆայլ, որն աշխատում էր էմուլատորում (TRS-80 Model III):

Ինչպես ես մագնիսական ժապավենից վերականգնեցի տվյալները անհայտ ձևաչափով

Ես նախագծել եմ փոխակերպման կոմունալ ծրագրի վերջին տարբերակը՝ առաջին զարկերակի ավտոմատ որոշմամբ և հղման իմպուլսների միջև հեռավորությունը որպես GEM փաթեթ, աղբյուրի կոդը հասանելի է այստեղ Github.

Ամփոփում

Մեր անցած ճանապարհը հետաքրքիր ճանապարհորդություն էր դեպի անցյալ, և ես ուրախ եմ, որ վերջում գտա պատասխանը։ Ի թիվս այլ բաների, ես.

  • Ես պարզեցի ZX Spectrum-ում տվյալների պահպանման ձևաչափը և ուսումնասիրեցի ներկառուցված ROM-ի ռեժիմները աուդիո ձայներիզներից տվյալները պահելու/ընթերցելու համար:
  • Ես ծանոթացա TRS-80 համակարգչի և դրա տեսակների հետ, ուսումնասիրեցի օպերացիոն համակարգը, նայեցի նմուշային ծրագրերին և նույնիսկ հնարավորություն ունեցա վրիպազերծել մեքենայի կոդերում (ի վերջո, բոլոր Z80 մնեմոնիկները ինձ ծանոթ են)
  • Գրել է լիարժեք գործիք՝ աուդիո ձայնագրությունները CAS ձևաչափի փոխարկելու համար, որը կարող է կարդալ տվյալներ, որոնք չեն ճանաչվում «պաշտոնական» ծրագրի կողմից

Source: www.habr.com

Добавить комментарий