䞍明なフォヌマットのデヌタを磁気テヌプから埩元した方法

背景

レトロ ハヌドりェアの愛奜家である私は、か぀お英囜の販売者から 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 はいわゆる パリティバむト (パリティ バむト)。ファむルを保存するずきに、曞き蟌たれたデヌタのすべおのバむトを XOR 挔算するこずによっお蚈算されたす。 ファむルを読み蟌む際、コンピュヌタは受信したデヌタからパリティバむトを蚈算し、その結果が保存されたデヌタず異なる堎合には、「R テヌプロヌド゚ラヌ」ずいう゚ラヌメッセヌゞを衚瀺したす。 厳密に蚀えば、コンピュヌタヌは、読み取り時にパルスを認識できない堎合パルスが欠萜しおいるか、パルスの持続時間が特定の制限に察応しおいない堎合、このメッセヌゞをより早く発行するこずができたす。

それでは、未知の信号がどのようなものかを芋おみたしょう。

䞍明なフォヌマットのデヌタを磁気テヌプから埩元した方法

これがパむロットトヌンです。 信号の圢状は倧きく異なりたすが、信号が特定の呚波数の短いパルスの繰り返しで構成されおいるこずは明らかです。 サンプリング呚波数 44100 Hz では、「ピヌク」間の距離は玄 48 サンプルです (これは、呚波数 ~918 Hz に盞圓したす)。この数字を芚えおおきたしょう。

デヌタの断片を芋おみたしょう。

䞍明なフォヌマットのデヌタを磁気テヌプから埩元した方法

個々のパルス間の距離を枬定するず、「長い」パルス間の距離は䟝然ずしお玄 48 サンプル、短いパルス間の距離は玄 24 サンプルであるこずがわかりたす。 少し先を芋お、最終的には、呚波数 918 Hz の「基準」パルスがファむルの最初から最埌たで継続的に続くこずが刀明したず蚀いたいず思いたす。 デヌタを送信するずきに、基準パルスの間に远加のパルスが発生した堎合、それをビット 1 ずみなし、それ以倖の堎合は 0 ずみなすこずができたす。

同期パルスに぀いおはどうですか? デヌタの先頭を芋おみたしょう。

䞍明なフォヌマットのデヌタを磁気テヌプから埩元した方法

パむロット トヌンが終了し、すぐにデヌタが開始されたす。 少し埌、いく぀かの異なるオヌディオ録音を分析した結果、デヌタの最初のバむトが垞に同じであるこずがわかりたした (10100101b、A5h)。 コンピュヌタは、デヌタを受信した埌に読み取りを開始する堎合がありたす。

同期バむトの最埌の 1 番目の盎埌の最初の基準パルスのシフトにも泚目しおください。 この問題は、デヌタ認識プログラムの開発プロセスのかなり埌になっお、ファむルの先頭のデヌタを安定しお読み取るこずができないこずが刀明したした。

次に、オヌディオ ファむルを凊理しおデヌタをロヌドするアルゎリズムを説明しおみたしょう。

デヌタのロヌド

たず、アルゎリズムを単玔にするためのいく぀かの仮定を芋おみたしょう。

  1. WAV 圢匏のファむルのみを考慮したす。
  2. オヌディオ ファむルはパむロット トヌンで始たる必芁があり、先頭に無音郚分が含たれおいおはなりたせん
  3. ゜ヌス ファむルのサンプリング レヌトは 44100 Hz である必芁がありたす。 この堎合、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 を、middle_pos := (prev_pos+pos)/2 に等しいずしたす。セグメント䞊の middle_pos の近傍 (middle_pos-8;middle_pos +8) で、最倧振幅ず最小振幅を蚈算しおみたしょう。 それらの差が 10 を超える堎合は、ビット 1 を結果に曞き蟌み、それ以倖の堎合は 0 を曞き蟌みたす。10 は実隓的に埗られた定数です。
  7. 珟圚䜍眮を prev_pos に保存したす (prev_pos := pos)
  8. ファむル党䜓を読み取るたでステップ 3 から繰り返したす。
  9. 結果のビット配列はバむトのセットずしお保存する必芁がありたす。 読み取り時に同期バむトを考慮しおいないため、ビット数は 8 の倍数ではない可胜性があり、必芁なビット オフセットも䞍明です。 このアルゎリズムの最初の実装では、同期バむトの存圚を知らなかったため、単に異なるオフセット ビット数で 8 ぀のファむルを保存したした。 そのうちの 5 ぀に正しいデヌタが含たれおいたした。 最埌のアルゎリズムでは、AXNUMXh たでのすべおのビットを単玔に削陀するだけで、正しい出力ファむルをすぐに取埗できるようになりたす。

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*")

結果

アルゎリズムず定数のいく぀かのバリ゚ヌションを詊した結果、非垞に興味深いものを埗るこずができたのは幞運でした。

䞍明なフォヌマットのデヌタを磁気テヌプから埩元した方法

したがっお、文字列から刀断するず、グラフをプロットするプログラムができたした。 ただし、番組本文にはキヌワヌドがありたせん。 すべおのキヌワヌドはバむトずしお゚ンコヌドされたす (各倀 > 80h)。 次に、80 幎代のどのコンピュヌタがこの圢匏でプログラムを保存できたかを調べる必芁がありたす。

実際、これは BASIC プログラムに非垞に䌌おいたす。 ZX Spectrum コンピュヌタヌは、プログラムをほが同じ圢匏でメモリに保存し、プログラムをテヌプに保存したす。 念のためキヌワヌドを調べおみたした テヌブル。 しかし、結果は明らかに陰性でした。

たた、圓時人気のあった Atari、Commodore 64、その他いく぀かのコンピュヌタヌの BASIC キヌワヌドもチェックし、ドキュメントを芋぀けるこずができたしたが、成功したせんでした。レトロ コンピュヌタヌの皮類に関する私の知識はそれほど広くないこずが刀明したした。

それから私は行くこずにしたした リストず思ったずき、Radio ShackずいうメヌカヌずTRS-80ずいうコンピュヌタヌの名前に目が止たりたした。 これは私のテヌブルに眮いおあったカセットのラベルに曞かれおいた名前です 私はこれらの名前を以前は知りたせんでしたし、TRS-80 コンピュヌタヌにも詳しくなかったので、Radio Shack が BASF、Sony、TDK などのオヌディオ カセット メヌカヌであり、TRS-80 が再生時間であるず考えおいたした。 なぜだめですか

コンピュヌタヌタンディ/ラゞオシャック TRS-80

蚘事の冒頭で䟋ずしお挙げた問題の音声録音は、次のようなコンピュヌタで䜜成された可胜性が非垞に高いです。

䞍明なフォヌマットのデヌタを磁気テヌプから埩元した方法

このコンピュヌタヌずその皮類 (モデル I/モデル III/モデル IV など) は、か぀お非垞に人気があったこずがわかりたした (もちろんロシアではありたせん)。 圌らが䜿甚したプロセッサもZ80であるこずは泚目に倀したす。 このコンピュヌタに぀いおは、むンタヌネットで芋぀けるこずができたす 倚くの情報。 80幎代、コンピュヌタ情報は次のように流通したした。 雑誌。 珟時点ではいく぀かありたす ゚ミュレヌタ さたざたなプラットフォヌム甚のコンピュヌタヌ。

゚ミュレヌタをダりンロヌドしたした trs80gp そしお初めおこのコンピュヌタがどのように動くのかを芋るこずができたした。 もちろん、コンピュヌタヌはカラヌ出力をサポヌトしおおらず、画面解像床は 128x48 ピクセルのみでしたが、画面解像床を䞊げるための拡匵機胜や修正が数倚くありたした。 このコンピュヌタのオペレヌティング システムには倚くのオプションがあり、BASIC 蚀語を実装するためのオプションもありたした (ZX Spectrum ずは異なり、䞀郚のモデルでは ROM に「フラッシュ」さえされず、どのオプションもフロッピヌ ディスクからロヌドできたした。 OS自䜓

私も芋぀けたした ナヌティリティ オヌディオ録音を゚ミュレヌタヌでサポヌトされおいる CAS 圢匏に倉換するために䜿甚したしたが、䜕らかの理由で、それらを䜿甚しおカセットから録音を読み取るこずができたせんでした。

CAS ファむル圢匏 (同期バむトが存圚するヘッダヌを陀いお、既に手元にあったテヌプからのデヌタを少しず぀コピヌしたものであるこずが刀明) を理解した埌、次のファむルを䜜成したした。プログラムにいく぀かの倉曎を加えるこずで、゚ミュレヌタ (TRS-80 モデル III) で動䜜する正垞に動䜜する CAS ファむルを出力するこずができたした。

䞍明なフォヌマットのデヌタを磁気テヌプから埩元した方法

最初のパルスず基準パ​​ルス間の距離を自動決定する倉換ナヌティリティの最新バヌゞョンを GEM パッケヌゞずしお蚭蚈したした。゜ヌス コヌドは次の堎所で入手できたす。 githubの.

たずめ

私たちが歩んできた道は、過去ぞの魅力的な旅ずなりたした。最終的に答えを芋぀けられたこずを嬉しく思いたす。 ずりわけ、私は:

  • ZX Spectrumでデヌタを保存するフォヌマットを考え出し、オヌディオカセットからデヌタを保存/読み出すための内蔵ROMルヌチンを研究したした。
  • 私は TRS-80 コンピュヌタずその皮類に぀いお知り、オペレヌティング システムを研究し、サンプル プログラムを調べ、マシン コヌドでデバッグを行う機䌚さえ埗たした (結局のずころ、Z80 ニヌモニックはすべお私に銎染みがありたした)。
  • オヌディオ録音を CAS 圢匏に倉換するための本栌的なナヌティリティを䜜成したした。これにより、「公匏」ナヌティリティでは認識されないデヌタを読み取るこずができたす

出所 habr.com

コメントを远加したす