File size: 5,513 Bytes
495ab87
 
85d57e6
427d509
0c2ec94
e17912f
7d8a53d
0c2ec94
a4fc946
495ab87
7d8a53d
 
1bf845b
 
 
 
 
 
 
 
 
21a72bc
 
7d8a53d
 
21a72bc
e17912f
21a72bc
 
 
 
0c2ec94
21a72bc
e17912f
21a72bc
 
7d8a53d
e17912f
1bf845b
 
e17912f
0c2ec94
e17912f
7d8a53d
0c2ec94
e17912f
1bf845b
e17912f
 
0c2ec94
e17912f
85d57e6
 
7d8a53d
a4fc946
85d57e6
495ab87
 
 
 
 
 
 
 
 
 
7d8a53d
85d57e6
 
0c2ec94
 
 
 
85d57e6
495ab87
 
7d8a53d
 
 
 
 
 
 
64bc281
 
495ab87
0c2ec94
 
 
7d8a53d
 
46fc6dc
495ab87
 
 
7ecfca4
495ab87
64bc281
 
 
 
 
 
 
 
7ecfca4
d1fd039
7d8a53d
64bc281
 
 
 
 
 
7ecfca4
 
495ab87
 
 
85d57e6
495ab87
85d57e6
64bc281
 
 
 
 
495ab87
 
 
 
 
 
 
96d90f6
495ab87
 
 
 
427d509
 
85d57e6
495ab87
 
 
85d57e6
495ab87
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import numpy as np
import wave
from scipy.signal import find_peaks
from scipy.fft import fft, fftfreq
import gradio as gr
import tempfile
from PIL import Image
import io

def log_message(message):
    print(message)

def smooth_signal(signal, window_len=11):
    """Apply a smoothing filter to the signal to reduce noise."""
    if window_len < 3:
        return signal
    s = np.r_[signal[window_len-1:0:-1], signal, signal[-2:-window_len-1:-1]]
    window = np.hanning(window_len)
    smoothed = np.convolve(window/window.sum(), s, mode='valid')
    return smoothed

def detect_beeps(audio_data, framerate, freq_zero, freq_one, window_size=2048, step_size=512):
    log_message("Starting beep detection with sliding window.")
    
    binary_data = bytearray()
    num_windows = len(audio_data) // step_size
    previous_bit = None
    
    for i in range(0, len(audio_data) - window_size, step_size):
        # Get the window of data
        window = audio_data[i:i + window_size]
        
        # Perform FFT to detect the dominant frequency in the window
        yf = fft(window)
        xf = fftfreq(window_size, 1 / framerate)
        magnitude = np.abs(yf[:window_size // 2])
        dominant_freq = xf[np.argmax(magnitude)]
        
        # Classify based on the dominant frequency
        if np.isclose(dominant_freq, freq_zero, atol=10):
            current_bit = 0
        elif np.isclose(dominant_freq, freq_one, atol=10):
            current_bit = 1
        else:
            continue  # Skip if it doesn't match expected frequencies
        
        # Only record the bit if it differs from the previous bit (avoids repetition)
        if current_bit != previous_bit:
            binary_data.append(current_bit)
            log_message(f"Detected bit: {current_bit} (Frequency: {dominant_freq} Hz)")
            previous_bit = current_bit
    
    log_message("Finished beep detection.")
    return binary_data

def audio_to_binary(audio_file, freq_zero, freq_one):
    log_message("Starting audio_to_binary function.")
    
    # Read the audio file
    with wave.open(audio_file.name, 'rb') as wav_file:
        framerate = wav_file.getframerate()
        n_frames = wav_file.getnframes()
        audio_data = wav_file.readframes(n_frames)
    
    audio_data = np.frombuffer(audio_data, dtype=np.int16).astype(np.float32)
    audio_data = audio_data / np.max(np.abs(audio_data))  # Normalize to [-1, 1]
    
    # Detect beeps and reconstruct binary data
    binary_data = detect_beeps(audio_data, framerate, freq_zero, freq_one)
    
    if not binary_data:
        return None, "No binary data detected."
    
    # Convert binary list to bytes
    bit_accumulator = 0
    bit_count = 0
    final_binary_data = bytearray()
    
    for bit in binary_data:
        bit_accumulator = (bit_accumulator << 1) | bit
        bit_count += 1
        if bit_count == 8:
            final_binary_data.append(bit_accumulator)
            bit_count = 0
            bit_accumulator = 0
    
    if bit_count > 0:
        final_binary_data.append(bit_accumulator << (8 - bit_count))
    
    log_message(f"Extracted binary data: {final_binary_data[:20]}... (truncated for log)")
    return final_binary_data

def binary_to_file(binary_data, file_format):
    log_message("Starting binary_to_file function.")
    
    temp_file_path = None
    if file_format == 'PNG':
        try:
            image = Image.open(io.BytesIO(binary_data))
            with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as temp_file:
                image.save(temp_file, format='PNG')
                temp_file_path = temp_file.name
            log_message("Successfully reconstructed PNG file.")
        except Exception as e:
            log_message(f"Failed to reconstruct PNG file: {e}")
    elif file_format == 'TXT':
        try:
            text_data = binary_data.decode('utf-8', errors='replace')
            with tempfile.NamedTemporaryFile(delete=False, suffix='.txt') as temp_file:
                temp_file.write(text_data.encode('utf-8'))
                temp_file_path = temp_file.name
            log_message("Successfully reconstructed TXT file.")
        except Exception as e:
            log_message(f"Failed to reconstruct TXT file: {e}")
    else:
        log_message("Unsupported file format.")
    
    return temp_file_path

def process_audio(file, freq_zero, freq_one, file_format):
    log_message("Starting process_audio function.")
    binary_data = audio_to_binary(file, freq_zero, freq_one)
    
    if not binary_data:
        log_message("No binary data was extracted from the audio.")
        return None, "No data was extracted from the audio file."
    
    original_file_path = binary_to_file(binary_data, file_format)
    
    if original_file_path is None:
        return None, "Error in reconstructing the file."
    
    log_message("Processing complete.")
    return original_file_path, "File successfully reconstructed."

gr.Interface(
    fn=process_audio,
    inputs=[
        gr.File(label="Upload Binary WAV File"),
        gr.Number(label="Frequency for 0 (Hz)", value=1000),
        gr.Number(label="Frequency for 1 (Hz)", value=2000),
        gr.Dropdown(choices=['PNG', 'TXT'], label="File Format")
    ],
    outputs=[gr.File(label="Download Original File"), gr.Text(label="Status")],
    title="Binary WAV to Original File Decoder",
    description="Detect beeps with different frequencies in a binary WAV file to reconstruct the original file."
).launch()