Prehistorya
Retro avadanlığın həvəskarı olduğum üçün bir dəfə Böyük Britaniyadakı bir satıcıdan ZX Spectrum+ aldım. Kompüterin özü ilə birlikdə mən oyunları olan bir neçə audio kaset aldım (təlimatlar ilə orijinal qablaşdırmada), həmçinin xüsusi işarələri olmayan kasetlərdə yazılmış proqramlar. Təəccüblüdür ki, 40 illik kasetlərdən olan məlumatlar yaxşı oxunurdu və mən onlardan demək olar ki, bütün oyunları və proqramları yükləyə bildim.
Bununla belə, bəzi kasetlərdə ZX Spectrum kompüteri tərəfindən açıq-aydın edilməmiş yazılar tapdım. Onlar tamamilə fərqli səslənirdilər və qeyd olunan kompüterdəki yazılardan fərqli olaraq, ümumiyyətlə bütün proqramların və oyunların qeydlərində olan qısa BASIC yükləyicisi ilə başlamadılar.
Bir müddət bu məni təqib etdi - mən həqiqətən onlarda nəyin gizləndiyini öyrənmək istədim. Əgər siz audio siqnalı bayt ardıcıllığı kimi oxuya bilsəniz, simvolları və ya siqnalın mənşəyini göstərən hər hansı bir şeyi axtara bilərsiniz. Bir növ retro-arxeologiya.
İndi mən bütün yolu getdim və kasetlərin etiketlərinə baxdım, gülümsəyirəm, çünki
cavab gözümün qabağında idi
Sol kasetin etiketində TRS-80 kompüterinin adı və istehsalçının adının bir az altında: “ABŞ-da Radio Shack tərəfindən istehsal edilmişdir”
(Əgər intriqanı sona qədər saxlamaq istəyirsinizsə, spoylerin altına düşməyin)
Səs siqnallarının müqayisəsi
Əvvəlcə səs yazılarını rəqəmsallaşdıraq. Bunun nə kimi səsləndiyini dinləyə bilərsiniz:
Həmişə olduğu kimi ZX Spectrum kompüterindən səs yazısı səslənir:
Hər iki halda, qeydin əvvəlində sözdə bir şey var pilot tonu - eyni tezlikli səs (ilk qeyddə çox qısa <1 saniyə, lakin fərqləndirilir). Pilot ton kompüterə məlumatı qəbul etməyə hazırlaşmaq üçün siqnal verir. Bir qayda olaraq, hər bir kompüter siqnalın forması və tezliyi ilə yalnız "öz" pilot tonunu tanıyır.
Siqnal formasının özü haqqında bir şey söyləmək lazımdır. Məsələn, ZX Spectrum-da onun forması düzbucaqlıdır:
Pilot ton aşkar edildikdə, ZX Spectrum siqnalın tanındığını göstərmək üçün ekranın sərhədində alternativ qırmızı və mavi çubuqları göstərir. Pilot tonu bitir sinxron nəbz, kompüterə məlumat qəbul etməyə başlamaq üçün siqnal verir. Daha qısa müddət (pilot tonu və sonrakı məlumatlar ilə müqayisədə) ilə xarakterizə olunur (şəklə bax)
Sinxronizasiya impulsu qəbul edildikdən sonra kompüter siqnalın hər bir yüksəlişini/düşməsini qeyd edir, müddətini ölçür. Müddət müəyyən limitdən azdırsa, bit 1 yaddaşa yazılır, əks halda 0. Bitlər baytlarda toplanır və N bayt alınana qədər proses təkrarlanır. N sayı adətən yüklənmiş faylın başlığından götürülür. Yükləmə ardıcıllığı aşağıdakı kimidir:
- pilot tonu
- başlıq (sabit uzunluq), yüklənmiş məlumatın ölçüsünü (N), fayl adını və növünü ehtiva edir
- pilot tonu
- məlumatların özü
Məlumatların düzgün yükləndiyinə əmin olmaq üçün ZX Spectrum sözdə oxuyur paritet bayt (paritet baytı), yazılı məlumatların bütün baytlarını XORing etməklə faylı saxlayarkən hesablanır. Faylı oxuyarkən, kompüter alınan məlumatlardan paritet baytını hesablayır və nəticə saxlanandan fərqlidirsə, "R Tape yükləmə xətası" səhv mesajını göstərir. Düzünü desək, kompüter oxuyarkən nəbzi tanıya bilmədikdə (buraxılmış və ya onun müddəti müəyyən məhdudiyyətlərə uyğun gəlmirsə) bu mesajı daha əvvəl verə bilər.
Beləliklə, indi naməlum siqnalın necə göründüyünü görək:
Bu pilot tondur. Siqnalın forması əhəmiyyətli dərəcədə fərqlidir, lakin aydındır ki, siqnal müəyyən tezlikdə təkrarlanan qısa impulslardan ibarətdir. 44100 Hz seçmə tezliyində “zirvələr” arasındakı məsafə təqribən 48 nümunədir (bu, ~918 Hz tezliyinə uyğundur).Gəlin bu rəqəmi xatırlayaq.
İndi məlumat fraqmentinə baxaq:
Fərdi impulslar arasındakı məsafəni ölçsək, məlum olur ki, "uzun" impulslar arasındakı məsafə hələ də ~48 nümunə, qısa olanlar arasında isə ~24 nümunədir. Bir az irəliyə baxaraq deyim ki, sonda məlum oldu ki, 918 Hz tezliyi olan “istinad” impulsları faylın əvvəlindən sonuna qədər davamlı olaraq izləyir. Ehtimal etmək olar ki, verilənlərin ötürülməsi zamanı istinad impulsları arasında əlavə impulsla rastlaşdıqda onu bit 1, əks halda 0 hesab edirik.
Sinxronizasiya nəbzi haqqında nə demək olar? Gəlin məlumatların əvvəlinə baxaq:
Pilot ton bitir və məlumat dərhal başlayır. Bir az sonra, bir neçə fərqli səs yazısını təhlil etdikdən sonra, məlumatın ilk baytının həmişə eyni olduğunu kəşf edə bildik (10100101b, A5h). Kompüter məlumatları qəbul etdikdən sonra oxumağa başlaya bilər.
Sinxronizasiya baytında sonuncu 1-cidən dərhal sonra ilk istinad nəbzinin yerdəyişməsinə də diqqət yetirə bilərsiniz. O, daha sonra məlumatların tanınması proqramının işlənib hazırlanması prosesində, faylın əvvəlindəki verilənləri sabit oxumaq mümkün olmadığı zaman aşkar edilmişdir.
İndi audio faylı emal edəcək və verilənləri yükləyəcək alqoritmi təsvir etməyə çalışaq.
Data Yüklənir
Əvvəlcə alqoritmi sadə saxlamaq üçün bir neçə fərziyyəyə baxaq:
- Biz yalnız WAV formatında olan faylları nəzərdən keçirəcəyik;
- Audio fayl pilot tonla başlamalı və başlanğıcda səssizliyi ehtiva etməməlidir
- Mənbə faylının seçmə sürəti 44100 Hz olmalıdır. Bu halda, 48 nümunənin istinad impulsları arasındakı məsafə artıq müəyyən edilmişdir və onu proqramlı şəkildə hesablamağa ehtiyac yoxdur;
- Nümunə formatı istənilən ola bilər (8/16 bit/üzən nöqtə) - çünki oxuyarkən onu istədiyinizə çevirə bilərik;
- Güman edirik ki, mənbə faylı amplituda ilə normallaşdırılıb, nəticə sabitləşməlidir;
Oxuma alqoritmi aşağıdakı kimi olacaq:
- Faylı yaddaşa oxuyuruq, eyni zamanda nümunə formatını 8 bitə çeviririk;
- Səs məlumatında ilk nəbzin yerini müəyyənləşdirin. Bunu etmək üçün nümunənin sayını maksimum amplituda ilə hesablamaq lazımdır. Sadəlik üçün onu bir dəfə əl ilə hesablayacağıq. Gəlin onu prev_pos dəyişəninə saxlayaq;
- Son nəbzin mövqeyinə 48 əlavə edin (pos := prev_pos + 48)
- Mövqeyi 48-ə artırmaq növbəti istinad nəbzinin mövqeyinə (lent qüsurları, lent ötürücü mexanizminin qeyri-sabit işləməsi və s.) Bunun üçün kiçik bir məlumat parçası götürün (pos-8;pos+8) və onun üzərindəki maksimum amplituda dəyərini tapın. Maksimuma uyğun mövqe pos-da saxlanılacaq. Burada 8 = 48/6 eksperimental olaraq əldə edilmiş sabitdir, bu, düzgün maksimumu təyin edəcəyimizə və yaxınlıqda ola biləcək digər impulslara təsir etməyəcəyimizə zəmanət verir. Çox pis hallarda, impulslar arasındakı məsafə 48-dən çox az və ya daha çox olduqda, nəbz üçün məcburi axtarış həyata keçirə bilərsiniz, lakin məqalə çərçivəsində bunu alqoritmdə təsvir etməyəcəyəm;
- Əvvəlki addımda, həmçinin istinad nəbzinin ümumiyyətlə tapıldığını yoxlamaq lazımdır. Yəni, sadəcə maksimumu axtarırsınızsa, bu, impulsun bu seqmentdə mövcud olduğuna zəmanət vermir. Oxuma proqramının son tətbiqində bir seqmentdə maksimum və minimum amplituda dəyərləri arasındakı fərqi yoxlayıram və müəyyən bir həddi keçərsə, bir impulsun varlığını hesablayıram. Sual həm də istinad nəbzi tapılmadıqda nə etməkdir. 2 variant var: ya məlumat bitdi və sonra səssizlik baş verir, ya da bu oxu xətası hesab edilməlidir. Bununla belə, alqoritmi sadələşdirmək üçün bunu buraxacağıq;
- Növbəti addımda məlumat impulsunun (bit 0 və ya 1) mövcudluğunu müəyyən etməliyik, bunun üçün seqmentin ortasını (prev_pos;pos) orta_posu orta_pos := (prev_pos+pos)/2-yə bərabər götürürük və seqmentdə orta_posun bəzi məhəlləsində (middle_pos-8;middle_pos +8) maksimum və minimum amplitudu hesablayaq. Aralarındakı fərq 10-dan çox olarsa, nəticəyə bit 1 yazırıq, əks halda 0. 10 təcrübi yolla alınan sabitdir;
- Cari mövqeyi əvvəlki_postda saxlayın (əvvəlki_pos := pos)
- 3-cü addımdan başlayaraq bütün faylı oxuyana qədər təkrarlayın;
- Nəticə bit massivi bayt dəsti kimi saxlanmalıdır. Oxuyarkən sinxron baytını nəzərə almadığımız üçün bitlərin sayı 8-ə çox olmaya bilər və tələb olunan bit ofset də məlum deyil. Alqoritmin ilk tətbiqində mən sinxron baytın mövcudluğu haqqında bilmirdim və buna görə də sadəcə müxtəlif sayda ofset bitləri olan 8 faylı saxladım. Onlardan birində düzgün məlumatlar var idi. Son alqoritmdə mən sadəcə A5h-ə qədər olan bütün bitləri silirəm ki, bu da mənə dərhal düzgün çıxış faylını əldə etməyə imkan verir.
Maraqlananlar üçün Ruby-də alqoritm
Proqramı yazmaq üçün Ruby dilini seçdim, çünki... Mən çox vaxt bunun üzərində proqramlaşdırıram. Seçim yüksək performanslı deyil, lakin oxu sürətini mümkün qədər sürətli etmək vəzifəsi buna dəyər deyil.
# Используем 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*")
Nəticə
Alqoritmin və sabitlərin bir neçə variantını sınadıqdan sonra son dərəcə maraqlı bir şey əldə etmək şanslı oldum:
Beləliklə, simvol sətirlərinə əsasən, qrafiklərin qurulması üçün bir proqramımız var. Bununla belə, proqram mətnində açar sözlər yoxdur. Bütün açar sözlər bayt kimi kodlanır (hər dəyər > 80h). İndi 80-ci illərdən hansı kompüterin bu formatda proqramları saxlaya biləcəyini öyrənməliyik.
Əslində, o, BASIC proqramına çox bənzəyir. ZX Spectrum kompüteri proqramları təxminən eyni formatda yaddaşda saxlayır və proqramları lentə saxlayır. Hər ehtimala qarşı açar sözləri yoxladım
Mən də o dövrün məşhur Atari, Commodore 64 və bir neçə digər kompüterlərin BASIC açar sözlərini yoxladım, bunun üçün sənədləri tapa bildim, lakin uğur qazana bilmədim - retro kompüterlərin növləri haqqında biliklərim o qədər də geniş olmadığı ortaya çıxdı.
Sonra getməyə qərar verdim
Kompüter Tandy/Radio Shack TRS-80
Çox güman ki, məqalənin əvvəlində misal gətirdiyim sözügedən səs yazısı belə bir kompüterdə hazırlanıb:
Məlum oldu ki, bu kompüter və onun növləri (Model I/Model III/Model IV və s.) bir vaxtlar çox məşhur idi (əlbəttə, Rusiyada deyil). İstifadə etdikləri prosessorun da Z80 olması diqqət çəkir. Bu kompüter üçün İnternetdə tapa bilərsiniz
Emulatoru yüklədim
mən də tapdım
CAS fayl formatını (bu, sinxron baytın mövcudluğu ilə başlıq istisna olmaqla, əlimdə olan məlumatların sadəcə bir bit-bit nüsxəsi olduğu ortaya çıxdı) anladım. proqramıma bir neçə dəyişiklik etdi və emulyatorda işləyən işləyən CAS faylını çıxara bildim (TRS-80 Model III):
GEM paketi olaraq ilk nəbzin və istinad impulsları arasındakı məsafənin avtomatik müəyyən edilməsi ilə dönüşüm yardım proqramının ən son versiyasını tərtib etdim, mənbə kodu burada mövcuddur.
Nəticə
Keçdiyimiz yol keçmişə maraqlı səyahət oldu və mən şadam ki, sonunda cavabı tapdım. Digər şeylər arasında mən:
- Mən ZX Spectrum-da məlumatların saxlanması formatını tapdım və audio kasetlərdən məlumatların saxlanması/oxuması üçün daxili ROM qaydalarını öyrəndim.
- TRS-80 kompüteri və onun növləri ilə tanış oldum, əməliyyat sistemini öyrəndim, nümunə proqramlara baxdım və hətta maşın kodlarında sazlama etmək imkanım oldu (axı bütün Z80 mnemonikaları mənə tanışdır)
- Səs yazılarını CAS formatına çevirmək üçün "rəsmi" yardım proqramı tərəfindən tanınmayan məlumatları oxuya bilən tam hüquqlu bir yardım proqramı yazdı.
Mənbə: www.habr.com