Beispiel #1
0
def encode(input_filename):
    """Given a filename, read the bytes of the file and encode it
    into an array. The array (of type SAMPLE_TYPE) is returned.
    """

    input_file = open(input_filename, mode='rb')
    input_bytes = input_file.read()

    notes = []
    for byte in input_bytes:
        all_bits = []

        # Append each byte of the input, left to right, into the bit list.
        for i in range(8):
            if byte & (1 << (7 - i)) > 0:
                all_bits.append(1)
            else:
                all_bits.append(0)

        notes.append(all_bits)


    csn = 0             # the value of the chord seqence number
    all_chords = []     # a list of concatenated bits
    n = 0               # our position in the notes list

    while n < len(notes):
        chord = []

        # We write the nibbles of the control note first. The
        # chord sequence number goes in the high nibble.
        ctrl_note = ~csn << 4
        ctrl_nib = 0

        if n == 0:
            # We are looking at the first byte of the input file, so we
            # will include the START nibble in the control note.
            ctrl_nib = chords.CTRL_NIB_START

        elif n >= len(notes) - (chords.CHORD_SIZE - 1):
            # We are looking at one of the last bytes of the input
            # file, which means we will be writing the last chord. Therefore
            # we include the END nibble in the control note.
            ctrl_nib = chords.CTRL_NIB_END

        else:
            # We are writing a regular data chord, somewhere between the START
            # and END chords.

            ctrl_nib = chords.CTRL_NIB_DATA


        # The control nibble goes in the low nibble of the control note.
        ctrl_note |= ctrl_nib

        # Append the bits of the control note (left to right).
        chord += bits.to_bit_list(ctrl_note)

        # Increment the sequence number for the next chord.
        csn += 1

        # We will now append the bits of CHORD_SIZE - 1 more notes
        # to the chord, as long as there are that many bytes available
        # in the input file.
        i = 0
        while n < len(notes) and i < chords.CHORD_SIZE - 1:
            chord += notes[n]
            n += 1
            i += 1


        # If there are not enough notes to fill this chord, pad it
        # with zero notes. (This means that we ran out of bytes from
        # the input file while constructing the last chord.)
        while len(chord) % (chords.CHORD_SIZE * 8) != 0:
            chord += [0] * 8

        all_chords.append(chord)


    freq_map = bands.get_frequency_map()
    data = array.array(wavetools.SAMPLE_TYPE)

    # Play each chord into the WAVE buffer. The bits of each chord are played
    # from low frequencies to high frequencies.
    for c in all_chords:
        num_samples = chords.CHORD_LENGTH

        for s in range(num_samples):
            sample = 0

            for f in range(len(freq_map)):

                if c[f] > 0:
                    sample += (wavetools.MAX_AMPLITUDE / len(freq_map)) * \
                              sin(2 * pi * freq_map[f] * \
                                  s / wavetools.SAMPLE_RATE)

            data.append(int(sample))

    return data
Beispiel #2
0
def decode_chord(samples):
    """Given a list of samples, interpret the samples as one chord and
    return a tuple (ctrl, bytes) where ctrl is the control note byte
    in the chord and bytes is a list of bytes of the remaining data notes.
    """

    map = bands.get_frequency_map()

    spec = fourier.fft(samples)

    # Compute amplitudes for each sample in window.
    amps = []
    for i in range(len(spec)):
        amps.append(sqrt(pow(spec[i].real, 2) + pow(spec[i].imag, 2)))

    # Determine whether the amplitudes indicate 0 or 1. 
    j = 0
    chord = []
    inRange = False
    foundBit = False

    # For all indices under the Nyquist Limit.
    for i in range(len(amps) // 2):

        # While the frequency is within some range around
        # a frequency in the frequency map.
        while abs(map[j] - i * wavetools.SAMPLE_RATE / \
                  fourier.WINDOW_SIZE) <= FREQ_RANGE:

            inRange = True

            # If one of the amplitudes within this frequency range
            # is above a threshold, then the bit should be a 1.
            if amps[i] > AMP_THRESHOLD:
                foundBit = True
                break
            i += 1

        if inRange:
            chord.append(1 if foundBit else 0)
            inRange = False
            foundBit = False
            j += 1

        if j == len(map): break


    # From our list of bits in the chord, construct bytes by slicing the
    # bit list in byte-sized sublists and convert them to bytes.
    notes = []
    for n in range(chords.CHORD_SIZE):
        note_start = (n * 8)
        note_end = note_start + 8

        note_bits = chord[note_start:note_end]

        notes.append(bits.to_byte(note_bits))

    
    # The first note is the control note, so we return it separately
    # from the other notes.
    return (notes[0], notes[1:])