prehistory
Kasancewar ni mai son kayan aikin baya ne, na taɓa siyan ZX Spectrum+ daga wani mai siyarwa a Burtaniya. Tare da kwamfutar kanta, na sami kaset ɗin sauti da yawa waɗanda ke ɗauke da wasanni (a cikin marufi na asali tare da umarni), da kuma shirye-shiryen da aka yi rikodin su akan kaset ba tare da wani alama na musamman ba. Abin mamaki, bayanan da ke cikin kaset ɗin mai shekaru 40 suna da sauƙin karantawa, kuma na sami damar loda kusan dukkan wasannin da shirye-shiryen daga gare su.

Duk da haka, a wasu kaset, na sami rikodin da a bayyane yake ba kwamfutar ZX Spectrum ce ta yi ba. Sun yi sauti daban-daban kuma, ba kamar rikodin da aka yi daga kwamfutar da aka ambata a sama ba, ba su fara da ɗan gajeren BASIC loader wanda yawanci yake samuwa a cikin rikodin duk shirye-shirye da wasanni ba.
Wannan ya dame ni na ɗan lokaci—ina son sanin abin da ke ɓoye a cikinsu. Idan zan iya karanta siginar sauti a matsayin jerin byte, zan iya neman alamomi ko duk wani abu da zai nuna asalin siginar. Wani nau'in ilimin tarihi na baya-bayan nan.
Yanzu da na gama duba lakabin kaset ɗin, sai na yi murmushi domin
Amsar ta kasance daidai a gaban idanuna duk tsawon lokacin
Lakabin da ke gefen hagu yana ɗauke da sunan kwamfutar TRS-80, kuma a ƙasa da sunan mai ƙera: "An ƙera ta Radio Shack a Amurka"
(Idan kana son ci gaba da jin tsoro har zuwa ƙarshe, kada ka danna ɓarna)
Kwatanta siginar sauti
Da farko, bari mu canza rikodin sauti zuwa dijital. Za ku iya sauraron yadda yake sauti:
Kuma kamar yadda aka saba, rikodin daga kwamfutar ZX Spectrum yana sauti:
A duka biyun, a farkon rikodin akwai abin da ake kira sautin matukin jirgi — sauti ɗaya mai mita ɗaya (a rikodin farko, yana da ɗan gajere sosai, ƙasa da daƙiƙa 1, amma har yanzu ana iya ganinsa). Sautin matukin jirgi yana nuna wa kwamfutar alama don ta shirya karɓar bayanai. Yawanci, kowace kwamfuta tana gane sautin matukin jirgi ne kawai bisa ga siffar siginar ta da mitar ta.
Ya kamata a ambaci siffar siginar da kanta. Misali, a kan ZX Spectrum, siffarsa tana da murabba'i mai kusurwa huɗu:

Idan aka gano sautin matukin jirgi, ZX Spectrum yana nuna launuka ja da shuɗi a kan iyakar allon, wanda ke nuna cewa an gane siginar. Sautin matukin jirgi ya ƙare. bugun daidaitawa, wanda ke nuna wa kwamfuta alama don fara karɓar bayanai. Ana siffanta shi da ɗan gajeren lokaci (idan aka kwatanta da sautin gwaji da bayanan da ke biyo baya) (duba hoto).
Bayan an karɓi bugun sync, kwamfutar tana rubuta kowace ƙaruwa/faɗuwar siginar, tana auna tsawon lokacinta. Idan tsawon lokacin bai kai wani iyaka ba, ana rubuta bit 1 zuwa ƙwaƙwalwa; in ba haka ba, ana rubuta bit 0. Ana tattara bit ɗin zuwa bytes, kuma tsarin yana maimaitawa har sai an karɓi bytes na N. Yawancin lokaci ana ɗaukar lambar N daga kan fayil ɗin da ake lodawa. Jerin lodawa kamar haka:
- sautin matukin jirgi
- kanun labarai (tsawon da aka ƙayyade), ya ƙunshi girman bayanan da za a sauke (N), suna da nau'in fayil ɗin
- sautin matukin jirgi
- bayanan da kanta
Domin tabbatar da cewa an ɗora bayanai daidai, ZX Spectrum yana karanta abin da ake kira byte na daidai (parity byte), wanda ake ƙididdigewa lokacin adana fayil ta hanyar XORing duk byte na bayanan da aka rubuta. Lokacin karanta fayil, kwamfuta tana ƙididdige byte na parity daga bayanan da aka karɓa kuma, idan sakamakon ya bambanta da wanda aka ajiye, tana nuna saƙon kuskuren "R Tape loading error." A takaice dai, kwamfutar na iya nuna wannan saƙon da wuri idan ba za ta iya gane bugun jini ba yayin karatu (ya ɓace ko tsawon lokacinsa bai cika wasu iyakoki ba).
To, bari yanzu mu ga yadda siginar da ba a sani ba take:

Wannan sautin gwaji ne. Tsarin raƙuman ruwa ya bambanta sosai, amma a bayyane yake cewa siginar ta ƙunshi maimaita bugun gajerun bugun wani takamaiman mita. A ƙimar ɗaukar samfurin 44100 Hz, nisan da ke tsakanin "kololuwa" shine kimanin samfura 48 (wanda ya yi daidai da mitar ~918 Hz). Bari mu tuna da wannan adadi.
Bari mu kalli ɓangaren bayanai yanzu:

Idan muka auna nisan da ke tsakanin bugun jini ɗaya-ɗaya, za mu ga cewa nisan da ke tsakanin bugun jini mai "dogon" har yanzu yana ~48 samfura, yayin da tsakanin gajerun kuma yana ~24. Da na ɗan yi gaba kaɗan, zan ce ya zama cewa bugun "ma'auni" yana biye da mitar 918 Hz akai-akai, daga farko zuwa ƙarshen fayil ɗin. Ana iya ɗauka cewa yayin watsa bayanai, idan ƙarin bugun jini ya faru tsakanin bugun jini mai ma'ana, za mu ƙidaya shi a matsayin bit 1, in ba haka ba a matsayin 0.
Yaya batun bugun sync? Bari mu kalli farkon bayanan:

Sautin gwaji ya ƙare kuma bayanan sun fara nan take. Bayan ɗan lokaci, bayan nazarin rikodin sauti daban-daban, an gano cewa byte na farko na bayanai koyaushe iri ɗaya ne (10100101b, A5h). Wataƙila kwamfutar ta fara karanta bayanan bayan ta karɓe su.
Haka kuma za ku iya lura da canjin da aka samu a bugun farko na tunani nan da nan bayan 1 na ƙarshe a cikin byte na sync. An gano wannan daga baya a lokacin haɓaka shirin gane bayanai, lokacin da ba za a iya karanta bayanan da ke farkon fayil ɗin da inganci ba.
Yanzu bari mu yi ƙoƙarin bayyana tsarin da zai sarrafa fayil ɗin sauti da kuma loda bayanai.
Loading Data
Bari mu fara la'akari da wasu zato don sauƙaƙe algorithm ɗin:
- Za mu yi la'akari da fayiloli ne kawai a tsarin WAV;
- Fayil ɗin sauti dole ne ya fara da sautin gwaji kuma bai kamata ya ƙunshi shiru a farkon ba.
- Fayil ɗin tushe dole ne ya kasance yana da mitar samfurin 44100 Hz. A wannan yanayin, an riga an ƙayyade nisan da ke tsakanin bugun tunani na samfuran 48 kuma ba ma buƙatar ƙididdige shi ta hanyar shirye-shirye;
- Tsarin samfurin zai iya zama kowane (8/16 bit/floating point) - tunda lokacin karatu za mu iya canza shi zuwa wanda ake buƙata;
- Muna ɗauka cewa fayil ɗin tushe yana da tsari mai kyau, wanda zai daidaita sakamakon;
Algorithm ɗin karatu zai kasance kamar haka:
- Muna karanta fayil ɗin a cikin ƙwaƙwalwar ajiya, a lokaci guda muna canza tsarin samfurin zuwa rago 8;
- Muna tantance matsayin bugun farko a cikin bayanan sauti. Don yin wannan, muna buƙatar lissafin lambar samfurin tare da matsakaicin girman. Don sauƙi, za mu ƙididdige shi da hannu sau ɗaya. Za mu adana shi a cikin canjin prev_pos.
- Ƙara 48 zuwa matsayin bugun ƙarshe (pos := prev_pos + 48)
- Tunda ƙara matsayi da 48 ba ya tabbatar da cewa za mu buga bugun tunani na gaba (saboda lahani na tef, aikin tuƙin tef mara ƙarfi, da sauransu), muna buƙatar daidaita matsayin bugun pos. Don yin wannan, za mu ɗauki ƙaramin ɓangaren bayanai (pos-8; pos+8) kuma mu nemo matsakaicin ƙimar girma. Za mu adana matsayin da ya dace da matsakaicin a cikin pos. A nan, 8 = 48/6 wani tsayayyen tsari ne da aka samu ta hanyar gwaji wanda ke tabbatar da cewa za mu sami matsakaicin da ya dace kuma ba zai shafi sauran bugun da za su iya kasancewa kusa ba. A cikin mawuyacin hali, lokacin da nisan da ke tsakanin bugun ya yi ƙasa da 48 ko ya fi girma, za mu iya aiwatar da binciken bugun da aka tilasta, amma ba zan bayyana wannan a cikin algorithm don dalilan wannan labarin ba.
- A matakin da ya gabata, zai kuma zama dole a duba ko an sami bugun tunani da gaske. Wato, kawai neman matsakaicin ba ya tabbatar da cewa bugun jini yana nan a wani sashe da aka bayar. A cikin sabon aiwatar da shirin karatu na, ina duba bambanci tsakanin ƙimar girma da mafi ƙarancin girma a cikin wani sashe, kuma idan ya wuce wani iyaka, ina ƙidaya kasancewar bugun jini. Wata tambaya ita ce abin da za a yi idan ba a sami bugun tunani ba. Akwai zaɓuɓɓuka biyu: ko dai bayanan sun ƙare kuma akwai shiru, ko kuma wannan ya kamata a ɗauke shi a matsayin kuskuren karantawa. Duk da haka, za mu cire wannan don sauƙaƙe tsarin.
- Mataki na gaba shine a tantance kasancewar bugun bayanai (bit 0 ko 1). Don yin wannan, muna ɗaukar tsakiyar ɓangaren (prev_pos;pos) middle_pos daidai yake da middle_pos := (prev_pos+pos)/2 kuma mu ƙididdige matsakaicin da mafi ƙarancin girma a wasu kusanci na middle_pos akan sashin (middle_pos-8;middle_pos+8). Idan bambancin da ke tsakaninsu ya fi 10, muna rubuta bit 1 ga sakamakon; in ba haka ba, muna rubuta 0. 10 shine madaidaicin da aka samu ta hanyar gwaji;
- Ajiye matsayin da ake ciki a cikin prev_pos (prev_pos := pos)
- Mu maimaita tun daga mataki na 3 har sai mun karanta dukkan fayil ɗin;
- Dole ne a adana bitmap ɗin da ya fito a matsayin saitin byte. Tunda ba mu yi lissafin byte ɗin daidaitawa ba lokacin karantawa, adadin bit ɗin bazai zama ninki na 8 ba, kuma ba a san adadin bit ɗin da ake buƙata ba. A farkon aiwatar da algorithm ɗin, ban san game da byte ɗin daidaitawa ba don haka kawai na adana fayiloli takwas tare da lambobi daban-daban na bit ɗin daidaitawa. Ɗaya daga cikinsu ya ƙunshi bayanai daidai. A cikin algorithm na ƙarshe, kawai ina cire duk bit ɗin har zuwa A5h, wanda ke ba ni damar samun fayil ɗin fitarwa daidai nan take.
Tsarin lissafi a cikin Ruby, ga waɗanda ke da sha'awar
Na zaɓi Ruby a matsayin harshen shirye-shirye saboda ina ɓatar da mafi yawan lokacina a cikinsa na shirye-shirye. Ba zaɓi ne mai inganci ba, amma haɓaka saurin karatu ba shine burina ba.
# Используем 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*")
sakamakon
Bayan gwada bambance-bambancen algorithm da na yau da kullun da yawa, na yi sa'a na sami wani abu mai ban sha'awa sosai:

Don haka, idan aka yi la'akari da igiyoyin haruffa, muna da shirin tsara zane-zane. Duk da haka, rubutun shirin bai ƙunshi kalmomi masu mahimmanci ba. Duk kalmomin shiga an rubuta su a matsayin bytes (kowace ƙima > 80h). Yanzu muna buƙatar gano wace kwamfuta daga shekarun 80 za ta iya adana shirye-shirye a cikin wannan tsari.
A gaskiya ma, yana kama da shirin BASIC sosai. Kwamfutar ZX Spectrum tana amfani da kusan tsari ɗaya don adana shirye-shirye a cikin ƙwaƙwalwar ajiya da adana su a cikin tef. Idan akwai, na duba kalmomin shiga akan Duk da haka, sakamakon ya bayyana a fili mara kyau.
Na kuma duba kalmomin BASIC na kwamfutocin da suka shahara a lokacin, Atari, Commodore 64, da wasu kaɗan waɗanda na sami takardu, amma ba tare da nasara ba—ilimin da na samu game da nau'ikan kwamfutocin retro bai yi yawa ba.
Sai na yanke shawarar zuwa , sai na kalli sunan masana'anta—Radio Shack—da kuma kwamfutar TRS-80. Waɗannan su ne sunayen da aka rubuta a kan lakabin kaset ɗin da ke kan teburi na! Ban taɓa jin labarin waɗannan sunayen ba a da, kuma ban saba da kwamfutar TRS-80 ba, don haka na ɗauka cewa Radio Shack masana'antar kaset ne, kamar BASF, Sony, ko TDK, kuma TRS-80 shine lokacin wasa. Me yasa ba haka ba?
Tandy/Radio Shack Kwamfuta TRS-80
Akwai yiwuwar cewa an yi rikodin sauti da ake magana a kai, wanda na bayar a matsayin misali a farkon labarin, a kwamfuta kamar haka:

Ya bayyana cewa wannan kwamfutar da nau'ikanta (Model I/Model III/Model IV, da sauransu) sun shahara sosai a lokacinsu (ba a Rasha ba, ba shakka). Yana da kyau a lura cewa na'urar sarrafawa da suka yi amfani da ita ita ma Z80 ce. Ana iya samun bayanai game da wannan kwamfutar a yanar gizo. A shekarun 80, an yaɗa bayanai game da kwamfuta a Akwai wasu da yawa a yanzu kwamfutoci don dandamali daban-daban.
Na sauke emulator ɗin Kuma a karon farko, na sami damar ganin yadda wannan kwamfutar ke aiki. Tabbas, kwamfutar ba ta goyon bayan fitowar launi ba, kuma ƙudurin allo shine pixels 128x48 kawai, amma akwai ƙarin fasali da gyare-gyare da yawa waɗanda zasu iya ƙara ƙudurin allo. Akwai kuma nau'ikan tsarin aiki da yawa don wannan kwamfutar da aiwatar da yaren BASIC (wanda, ba kamar ZX Spectrum ba, ba a ma kunna shi cikin ROM a wasu samfura ba; kowane nau'in za a iya ɗora shi daga faifai mai faifai, kamar OS ɗin kanta).
Na kuma samu don canza rikodin sauti zuwa tsarin CAS, wanda masu kwaikwayon ke tallafawa, amma saboda wani dalili ban iya karanta rikodin daga kaset ɗina da su ba.
Bayan na gano tsarin fayil ɗin CAS (wanda ya zama kwafin bayanai kaɗan-da-bit daga cikin tef ɗin da na riga na mallaka, sai dai kanun da ke ɗauke da byte ɗin daidaitawa), na yi wasu canje-canje ga shirina kuma na sami damar samun fayil ɗin CAS mai aiki a wurin fitarwa, wanda ke aiki a cikin emulator (TRS-80 Model III):

Na tsara sabuwar sigar kayan aikin juyawa tare da gano bugun farko ta atomatik da kuma nisan da ke tsakanin bugun tunani a matsayin fakitin GEM, lambar tushe tana samuwa a .
ƙarshe
Tafiyar da aka yi zuwa yanzu ta kasance tafiya mai ban sha'awa zuwa baya, kuma ina farin ciki da na sami amsar a ƙarshe. Daga cikin wasu abubuwa, ni:
- Na gano tsarin adana bayanai a cikin ZX Spectrum kuma na yi nazarin tsarin ROM da aka gina don adanawa/karanta bayanai daga kaset ɗin sauti.
- Na saba da kwamfutar TRS-80 da nau'ikanta, na yi nazarin tsarin aiki, na duba samfuran shirye-shirye, har ma na sami damar gyara lambobin na'ura (bayan haka, na saba da duk abubuwan tunawa na Z80 sosai).
- Na rubuta cikakken kayan aiki don canza rikodin sauti zuwa tsarin CAS, wanda zai iya karanta bayanan da kamfanin "na hukuma" bai gane ba.
source: www.habr.com
