Dragon 32 Homecomputer Kassetten in ASCII umwandeln...

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@jerch: warum die ganze händische Bytearbeite? Wenn du auf Geschwindigkeit optimieren willst, dann bietet sich wohl eher eine Lookup-Tabelle an.

Code: Alles auswählen

def reverse_bits(b, length=8):
    r = 0
    for _ in range(length):
        r = (r << 1) | (b & 1)
        b >>= 1
    return r
Das Leben ist wie ein Tennisball.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also die Code Stücke werfen bei mir keine brauchbaren Daten aus. (Dumm, das Forum hat ja immer noch den & Bug)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Kannst Du die Rohdaten irgendwie zugänglich machen? Dann wirds vllt einfacher.
Ich frage mich hier sowieso, warum Du dass alles auf Bitebene machst. Kannst Du nicht einfach den initialen Offset des Headers bestimmen und dann auf alignment-korrigierten Bytes arbeiten?

@EyDu:
Ja iss'n fauler Kompromiss mit etwas Geschwindigkeitsvorteil, den ich in c gerne nutze. Ein lookup table ist natürlich schneller.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ist auf github: https://github.com/jedie/python-code-sn ... ragon%2032

Dort gibt es die erwähnten zwei WAV files, ebenfalls.

EDIT: Direkter download links der beiden WAV files von github:
https://github.com/jedie/python-code-sn ... origin.wav
https://github.com/jedie/python-code-sn ... 0xroar.wav

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab gerade nochmal einen pull des aktuellen Stands gemacht: https://github.com/jedie/python-code-sn ... 9b258cea6d

Hab ein BASIC token dict eingefügt, sieht so aus:

Code: Alles auswählen

BASIC_TOKENS = {
    128: " FOR ",     # 0x80
    129: " GO ",      # 0x81
    130: " REM ",     # 0x82
    131: "'",         # 0x83
    132: " ELSE ",    # 0x84
    133: " IF ",      # 0x85
...
Der wird genutzt zum umwandeln. Aber in der einfachen Art und weise:

Code: Alles auswählen

def block2ascii(bit_list):
    for block in iter_steps(bit_list, steps=8):
        byte_no = bits2ASCII(block)

        if byte_no in BASIC_TOKENS:
            character = BASIC_TOKENS[byte_no]
        else:
            character = chr(byte_no)

        print "%s %4s %3s %s" % (
            list2str(block), hex(byte_no), byte_no, repr(character)
        )
Raus kommt dann u.a. das:

Code: Alles auswählen

...
11100001 0x87 135 ' PRINT '
00000100 0x20  32 ' '
10010010 0x49  73 'I'
11011100 0x3b  59 ';'
01000100 0x22  34 '"'
00010010 0x48  72 'H'
10100010 0x45  69 'E'
00110010 0x4c  76 'L'
00110010 0x4c  76 'L'
11110010 0x4f  79 'O'
00000100 0x20  32 ' '
11101010 0x57  87 'W'
11110010 0x4f  79 'O'
01001010 0x52  82 'R'
00110010 0x4c  76 'L'
00100010 0x44  68 'D'
10000100 0x21  33 '!'
01000100 0x22  34 '"'
00000000  0x0   0 '\x00'
...

Das sieht man allerdings aktuell nur mit FILENAME = "HelloWorld1 xroar.wav" nicht mit der anderen WAVE...

Deswegen hab ich mal ein Test gemacht:

Code: Alles auswählen

    TEST_STR=(
        "00010010" # 0x48  72 'H'
        "10100010" # 0x45  69 'E'
        "00110010" # 0x4c  76 'L'
        "00110010" # 0x4c  76 'L'
        "11110010" # 0x4f  79 'O'
        "00000100" # 0x20  32 ' '
        "11101010" # 0x57  87 'W'
        "11110010" # 0x4f  79 'O'
        "01001010" # 0x52  82 'R'
        "00110010" # 0x4c  76 'L'
        "00100010" # 0x44  68 'D'
        "10000100" # 0x21  33 '!'
    )# 000100101010001000110010001100101111001000000100111010101111001001001010001100100010001010000100
    test_start = get_start_pos_iter_window(bit_list, TEST_STR)
    print "*** Test String found at:", test_start
In beiden WAV Dateien wird dieser Teil gefunden!

Also ist es wieder nicht synchron...

Aktuell wird auch nur nachgesehen wo sich START_LEADER = "10101010" befindet und dann alles in 8Bits zerhackt. Denke das ist nicht das richtige...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

[quote="jerch"]Hier mal in c-hackisch:

Code: Alles auswählen

...
  sentinel = c_ubyte()
  i_bits = iter(bits)
  for bit in i_bits:
    sentinel.value <<= 1
    sentinel.value |= bit
    if sentinel.value == 42: # ?? nicht spec-konform
      break
...[/quote]
Hab mir den code nochmal genauer angesehen.

Kann es sein, das im Grunde hier in dem Teil im Prinzip das selbe gemacht wird, wie bei mir? Also das man bit für bit durchgeht und ein bestimmtes Muster zu suchen? In diesem Fall halt [b]START_LEADER = "10101010"[/b]

Wobei ich mir nun überlegt habe, das gerade [b]10101010[/b] ziemlich doof ist. Denn die genau Position kann in diesem Fall doch sehr leicht um 2 Bits vor/zurück verschoben sein und doch passt es.

Bei http://www.onastick.clara.net/cosio.htm steht auch was von einem sync byte:
[b]1. A leader byte - $55
2. A sync byte - $3C[/b]

Kann es sein, das man besser das suchen sollte?

EDIT: Kann es sein, das 0x3C das ist: [b]00111100[/b] ?
Das würde auch mehr Sinn machen!

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ha! Ich denke genau das ist es!

Mit HelloWorld1 xroar.wav (was funktioniert):
*** file info block data:
00111100 00000000 11110000...
Mit HelloWorld1 origin.wav (was nicht funktioniert):
*** file info block data:
10101000 11110000 0000001...
Hab das "sync byte" mal unterstrichen. Wie es aussieht ist das "verschoben"...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

glaube das hilft:

Code: Alles auswählen

def goto_next_block(bit_list, debug=False):
    """
    >>> bits = (
    ... "10101010" # 0x55 leader byte
    ... "00111100" # 0x3C sync byte
    ... "00010010" # 0x48 'H'
    ... )
    >>> bit_list = [int(i) for i in bits]
    >>> bit_list = goto_next_block(bit_list)
    >>> bit_list
    [0, 0, 0, 1, 0, 0, 1, 0]

    more bits inserted:
    >>> bits = ("1010" # inserted
    ... "101010100011110000010010")
    >>> goto_next_block([int(i) for i in bits])
    [0, 0, 0, 1, 0, 0, 1, 0]

    no complete leader byte
    >>> bits = ("1010" # incomplete
    ... "0011110000010010")
    >>> goto_next_block([int(i) for i in bits])
    [0, 0, 0, 1, 0, 0, 1, 0]
    """
    end = get_last_pos_iter_steps(bit_list, LEADER_BYTE)
    if not end:
        if debug:
            print "INFO: leader byte block end not found."
    else:
        if debug:
            print "leader byte block end found at:", end
        bit_list = bit_list[end:]

    sync_pos = get_start_pos_iter_window(bit_list, SYNC_BYTE)
    if sync_pos is None:
        if debug:
            print "ERROR: Sync byte '%s' not found!" % SYNC_BYTE
        sys.exit(-1)
    if debug:
        print "Sync byte '%s' found at position: %i" % (SYNC_BYTE, sync_pos)

    # Cut until sync byte
    cut_pos = sync_pos + len(SYNC_BYTE)
    bit_list = bit_list[cut_pos:]

    return bit_list

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
snafu
User
Beiträge: 6862
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

jens hat geschrieben:EDIT: Kann es sein, das 0x3C das ist: 00111100 ?
Das würde auch mehr Sinn machen!
Na, das ist ja wohl schnell rauszufinden. Wenn man keine Lust hat, selber zu rechnen:

Code: Alles auswählen

>>> bin(0x3c)
'0b111100'
Also offensichtlich ist es so.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

snafu hat geschrieben:

Code: Alles auswählen

>>> bin(0x3c)
'0b111100'
Wie kann man eigentlich das auf "einfache" Weise auffüllen? Also das man wieder 00111100 erhält?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

Code: Alles auswählen

 "{:08b}".format(0x3c)
Zuletzt geändert von darktrym am Freitag 16. August 2013, 20:36, insgesamt 1-mal geändert.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
BlackJack

@darktrym: Die Frage war nach *einfach*. Oder gingst Du davon aus das die Anführungsstriche nach einer möglichst komplizierten Variante verlangten? :twisted:

Code: Alles auswählen

In [7]: '{0:08b}'.format(0x3c)
Out[7]: '00111100'
Benutzeravatar
darktrym
User
Beiträge: 785
Registriert: Freitag 24. April 2009, 09:26

Zuspät, ein Glas Bier hat den Weg zur Lösung verlängert.
„gcc finds bugs in Linux, NetBSD finds bugs in gcc.“[Michael Dexter, Systems 2008]
Bitbucket, Github
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@jens:
Ich hab mir mal Deine origin-Aufnahme angeschaut, Du hast da ein tieffrequentes Störsignal drin (Netzbrummen?). Es ist nicht sehr laut, könnte aber dazu führen, dass Du für die Nulldurchgänge die falsche Frequenz und damit falsche Bitsequenz berechnest. Damit Dein Wave-Decoder mit sowas zuverlässig umgehen kann, solltest Du einen Hochpassfilter einbauen.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Die Aufnahme die du meinst, hatte ich frisch digitalisiert. Also Dragon Rechner direkt in Soundkarte gepackt.

Aufnahmen die auf Kassette aufgenommen und dann davon wiedergegeben, dürften noch viel mehr Störgeräusche haben :)

Der aktuelle Code funktioniert aber mit beiden WAV Dateien die auf github sind.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Also ich bin schon eine ganze Ecke weiter gekommen. Den BASIC code von meinem Beispiel erhalte ich mittlerweile.

Doch so generell gehe ich wohl aus Unwissenheit nicht den best practice weg.

Ich erhalte ja über die WAVE Datei eine Liste von einsen und nullen... Darin muß ich ein "Sync Byte" finden und könnte dann wahrscheinlich alles zu einer Liste von bytes wandeln oder direkt einem binären String.
Doch bisher belasse ich es bei der "Bit-Liste" und wandel erst möglichst spät um.

Was würdet ihr machen?

Andere Frage: Der eigentliche BASIC-Code liegt grob in dieser Binären Form vor:

0x1e
0x00 <- Startmarkierung
0x?? <- Zeilennummer
...nun kommt eine Code Zeile...
0x00 <- Endmarkierung
0x1e
0x00 <- Startmarkierung
0x?? <- Zeilennummer
...Code Zeile...
0x00 <- Endmarkierung code zeile
0x00
0x00 <- zweimal 0x00 == Dateiende

Genauere Beschreibung hier: http://dragon32.info/info/basicfmt.html

z.Z. iteriere ich über die Bytes, schaue nach den Marker-Bytes und verarbeite es mit einigen IF...ELSE Blöcken, siehe: https://github.com/jedie/python-code-sn ... c12#L0R484

Wie kann man so was einfacher machen?

Denke das 'struct' Modul wäre was. Aber ich kann da nicht erkennen, wie man Daten mit variabler Länge verarbeiten kann.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Ich würde versuchen die Zustandsvariablen zu vermeiden aus einem Byteiterator die Informationen ziehen, die man braucht. Also zum Beispiel in einem ersten Schritt den Bytestrom in einen Strom von Zeilen umwandeln. Und dann eine Funktion schreiben, die eine einzelne Zeile ausgibt.

Teil der Lösung könnte zum Beispiel so eine Funktion sein:

Code: Alles auswählen

def iter_tokenized_lines(bytestream):
    while True:
        next_line_pointer = bytestream.get_word()
        if next_line_pointer == 0:
            break
        line_number = bytestream.get_word()
        tokenized_line = bytestream.get_until('\x00')
        yield (line_number, tokenized_line)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

jens hat geschrieben:Hab gerade nochmal einen pull des aktuellen Stands gemacht: https://github.com/jedie/python-code-sn ... 9b258cea6d

Hab ein BASIC token dict eingefügt, sieht so aus:

Code: Alles auswählen

BASIC_TOKENS = {
    128: " FOR ",     # 0x80
    129: " GO ",      # 0x81
    130: " REM ",     # 0x82
    131: "'",         # 0x83
    132: " ELSE ",    # 0x84
    133: " IF ",      # 0x85
...
Python hat auch Hex-Literale, die kann man auch bei Dicts als Schlüssel nutzen, dann spart man sich die seltsame Umrechnung und die Kommentare.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Das mache ich mittlerweile auch, siehe: https://github.com/jedie/python-code-sn ... _tokens.py ;)

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Hab nun verschiedene Beispiel-Wave Dateien mal getestet. Bekommt man u.a. hier her: http://archive.worldofdragon.org/archiv ... ragon/wav/

Sind alle i.d.R. größere WAVE Dateien als meine beiden Beispiele... Dann merkt man doch schnell, das es langsam ist ;)

Der Flaschenhals ist IMHO das umwandeln der WAVE Samles in nullen und einsen... Das kann man wiederrum in zwei Bereiche einteilen:
1. Das einlesen der WAV und erhalten der Sample-Werte
2. Das erkennen der nullen und einsen in den Sample-Werten

Mal Beispielwerte für eine WAVE Datei mit 5.151.803 audio samples:
Nr.1 dauert 1.5 sec
Nr.2 dauert 5.8 sec

Nr. 1 habe ich schon beschleunigt, in dem ich in größeren Blöcken struct.unpack füttere. Die Zeit dazu ist vollkommen ok.

Nr. 2 dauert recht lange. Der Zuständige Code dazu:

Code: Alles auswählen

def samples2bits(samples, framerate, frame_count, even_odd):
    in_positive = even_odd
    in_negative = not even_odd
    toggle_count = 0 # Counter for detect a complete cycle
    previous_frame_no = 0
    bit_count = 0

    window_values = collections.deque(maxlen=MIN_TOGGLE_COUNT)
    for frame_no, value in samples:
        window_values.append(value)
        if len(window_values) >= MIN_TOGGLE_COUNT:
            positive_count, negative_count = count_sign(window_values)

            # print window_values, positive_count, negative_count
            if not in_positive and positive_count == MIN_TOGGLE_COUNT and negative_count == 0:
                # go into a positive sinus area
                in_positive = True
                in_negative = False
                toggle_count += 1
            elif not in_negative and negative_count == MIN_TOGGLE_COUNT and positive_count == 0:
                # go into a negative sinus area
                in_negative = True
                in_positive = False
                toggle_count += 1

            if toggle_count >= 2:
                # a single sinus cycle complete
                toggle_count = 0

                frame_count = frame_no - previous_frame_no
                hz = framerate / frame_count

                dst_one = abs(ONE_HZ - hz)
                dst_nul = abs(NUL_HZ - hz)
                if dst_one < dst_nul:
                    bit_count += 1
                    yield 1
                else:
                    bit_count += 1
                    yield 0

                previous_frame_no = frame_no
(Kompletter code hier: https://github.com/jedie/python-code-sn ... _decode.py )

Ideen das zu beschleunigen?

Btw. in PyPy dürfte das eigentlich viel schneller gehen, oder? Muß ich mal irgendwann testen ;)


Eine Idee hätte ich noch: Eigentlich sind 44.1KHz Sampling Frequenz verschwendung. Es reicht auch die hälfte. Ich könnte also schon beim einlesen zwei Werte zusammen fassen oder zusammen fassen lassen.
Evtl. audioop.ratecv() nutzten?

Evtl. kann ich mit audioop.cross(fragment, width) was anfangen:
Return the number of zero crossings in the fragment passed as an argument.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Antworten