I am trying to solve a problem in my audio steganography code. Afted hiding the message in wav audio file, there is some noice which of course should not be there considering the point of the whole audio steganography. Thanks a lot for help !
here is the code
import wave
import os
global chosen_audio
def hide():
hide_menu()
global chosen_audio
message = input('Input message to be hidden: \n')
print('hiding message ... \n')
audio = wave.open(chosen_audio, 'rb')
frame_bytes = bytearray(list(audio.readframes(audio.getnframes())))
message = message + int((len(frame_bytes) - (len(message) * 8 * 8)) / 8) * '#'
bits = list(
map(int, ''.join([bin(ord(i)).lstrip('0b').rjust(8, '0') for i in message])))
for i, bit in enumerate(bits):
frame_bytes[i] = (frame_bytes[i] & 254) | bit # replace lsb
frames_modified = bytes(frame_bytes)
with wave.open('C:/Users/*****/PycharmProjects/steganography/modified_audio.wav', 'wb') as modified_audio:
modified_audio.setparams(audio.getparams())
modified_audio.writeframes(frames_modified)
print('message hidden ... \n')
modified_audio.close()
audio.close()
def reveal():
modified_audio = wave.open('C:/Users/*****/PycharmProjects/steganography/modified_audio.wav', 'rb')
frame_bytes = bytearray(list(modified_audio.readframes(modified_audio.getnframes())))
ls_bits = [frame_bytes[i] & 1 for i in range(len(frame_bytes))]
text = "".join(chr(int("".join(map(str, ls_bits[i:i + 8])), 2)) for i in range(0, len(ls_bits), 8))
message = text.split("###")[0]
modified_audio.close()
return message
def mode():
method = input(
' \n\t\t\tPLEASE, CHOOSE THE PROCEDURE! \n\n \tPRESS H FOR HIDING THE MESSAGE \t PRESS R FOR REVEALING THE MESSAGE FROM THE AUDIO\n')
if method == 'H' or method == 'h':
hide()
elif method == 'r' or method == 'R':
reveal()
else:
print('I don\'t think we have such a option')
mode()
def hide_menu():
global chosen_audio
chosen_option = ''
print(chosen_option)
chosen_option = ''
chosen_audio = ''
print(' \nCHOOSE YOUR AUDIO FILE! ')
chosen_option = (
input('\t press 1 & ENTER for your own audio path\n''\t press 2 & ENTER for default audio file\n'))
if chosen_option == '1':
file_path = input('Enter a file path: ')
if os.path.exists(file_path):
print('The path is okay, file exists!')
chosen_audio = file_path
else:
print('The specified file in this path does NOT exist')
hide_menu()
elif chosen_option == '2':
chosen_audio_option = input(
'\t press V & enter to use voice audio \t press S & enter to use sound audio\t press M & enter to use '
'song audio\n')
if chosen_audio_option == 'M' or chosen_audio_option == 'm':
chosen_audio = 'C:\\Users\\*****\\PycharmProjects\\steganography\\song_audio.wav'
elif chosen_audio_option == 'v' or chosen_audio_option == 'V':
chosen_audio = 'C:\\Users\\*****\\PycharmProjects\\steganography\\voice_audio.wav'
elif chosen_audio_option == 's' or chosen_audio_option == 'S':
chosen_audio = 'C:\\Users\\*****\\Desktop\\audio\\hracka_pes.wav'
else:
print('No such a option !')
hide_menu()
else:
print('I don\'t think we have such a option')
hide_menu()
def reveal_menu():
global chosen_audio
chosen_audio = ''
print(' \nCHOOSE YOUR AUDIO FILE! ')
chosen_option = int(
input('\t press 1 & ENTER for your own audio path\n''\t press 2 & ENTER for default audio file\n'))
if chosen_option == 1:
file_path = input('Enter a file path: ')
if os.path.exists(file_path):
print('The path is okay, file exists!')
chosen_audio = file_path
else:
print('The specified file in this path does NOT exist')
hide_menu()
elif chosen_option == 2:
pass
mode()
# menu()
# hide()
note - cannot use library for steganography
Hearing the noise in the modified_audio is the main problem
Your code is written to deal with wave files where sampwidth=1
, but per your comment, the sampwidth
of your file is 2
. This means that your frame_bytes
array is not an array of samples, it's an array of bytes in which every two bytes together form one sample. (And because nchannels
is 1
, there is one sample per frame, mono audio.) So when you changed the LSB of all the bytes, you were changing not only the LSB of each sample, but also a bit in the middle of it.
You should convert this to an array of samples, and then change the samples LSBs. You can use array
for this:
import array
samples = array.array('h', frame_bytes) # signed 16-bit
# ... modify the samples ...
frames_modified = samples.tobytes()
Of course, it would be best if your code checked beforehand if you're dealing with signed shorts (sampsize==2
) or unsigned bytes (sampsize==1
), and then handle them accordingly. You might do something like:
samples = array.array('h' if sampsize == 2 else 'B', frame_bytes)
for i, bit in enumerate(bits):
samples[i] = samples[i] & -2 | bit
(I have not tested this much)