Commit 300b492f authored by Stefan Westerfeld's avatar Stefan Westerfeld

Implemented block mix algorithm. Still missing multi-block support.

Signed-off-by: Stefan Westerfeld's avatarStefan Westerfeld <stefan@space.twc.de>
parent 3bda778c
......@@ -28,6 +28,7 @@ namespace Params
static double water_delta = 0.015; // strength of the watermark
static double pre_scale = 0.95; // rescale the signal to avoid clipping after watermark is added
static bool shuffle = false;
static bool mix = false;
static unsigned int seed = 0;
}
......@@ -146,6 +147,10 @@ parse_options (int *argc_p,
{
Params::shuffle = true;
}
else if (check_arg (argc, argv, &i, "--mix"))
{
Params::mix = true;
}
else if (check_arg (argc, argv, &i, "--seed", &opt_arg))
{
Params::seed = atoi (opt_arg);
......@@ -283,6 +288,19 @@ gen_shuffle (size_t size, int seed)
return result;
}
template<class T> void
gen_shuffle2 (vector<T>& result, int seed)
{
std::mt19937_64 rng = init_rng (seed); // should use cryptographically secure generator, properly seeded
// Fisher–Yates shuffle
for (size_t i = 0; i < result.size() - 1; i++)
{
size_t j = i + rng() % (result.size() - i);
std::swap (result[i], result[j]);
}
}
static unsigned char
from_hex_nibble (char c)
{
......@@ -441,6 +459,115 @@ add_watermark (const string& infile, const string& outfile, const string& bits)
vector<float> out_signal (wav_data.n_values());
printf ("channels: %d, samples: %zd, mix_freq: %f\n", wav_data.n_channels(), wav_data.n_values(), wav_data.mix_freq());
if (Params::mix)
{
struct MixEntry {
int frame;
int band;
};
vector<MixEntry> up_entries, down_entries;
for (int f = 0; f < Params::block * Params::frames_per_bit; f++)
{
vector<int> up;
vector<int> down;
get_up_down (f, up, down);
for (auto u : up)
up_entries.push_back ({ f, u });
for (auto d : down)
down_entries.push_back ({ f, d });
}
gen_shuffle2 (up_entries, /* seed */ 0);
gen_shuffle2 (down_entries, /* seed */ 1);
vector<vector<complex<float>>> fft_out;
vector<vector<complex<float>>> fft_delta_spect;
for (int f = 0; f < Params::block * Params::frames_per_bit; f++)
{
for (int ch = 0; ch < wav_data.n_channels(); ch++)
{
vector<float> frame = get_frame (wav_data, f, ch);
/* windowing */
double window_weight = 0;
for (size_t i = 0; i < frame.size(); i++)
{
const double fsize_2 = frame.size() / 2.0;
// const double win = window_cos ((i - fsize_2) / fsize_2);
const double win = window_hamming ((i - fsize_2) / fsize_2);
//const double win = 1;
frame[i] *= win;
window_weight += win;
}
/* to get normalized fft output corrected by window weight */
for (size_t i = 0; i < frame.size(); i++)
frame[i] *= 2.0 / window_weight;
/* FFT transform */
fft_out.push_back (fft (frame));
fft_delta_spect.push_back (vector<complex<float>> (fft_out.back().size()));
}
}
for (int f = 0; f < Params::block * Params::frames_per_bit; f++)
{
for (int ch = 0; ch < wav_data.n_channels(); ch++)
{
for (int frame_b = 0; frame_b < Params::bands_per_frame; frame_b++)
{
int b = f * Params::bands_per_frame + frame_b;
const int data_bit = bitvec[(f / Params::frames_per_bit) % bitvec.size()];
const double data_bit_sign = data_bit > 0 ? 1 : -1;
const int u = up_entries[b].band;
{
const float mag_factor = pow (abs (fft_out[up_entries[b].frame * wav_data.n_channels() + ch][u]), -Params::water_delta * data_bit_sign);
fft_delta_spect[up_entries[b].frame * wav_data.n_channels() + ch][u] = fft_out[up_entries[b].frame * wav_data.n_channels() + ch][u] * (mag_factor - 1);
}
const int d = down_entries[b].band;
{
const float mag_factor = pow (abs (fft_out[down_entries[b].frame * wav_data.n_channels() + ch][d]), Params::water_delta * data_bit_sign);
fft_delta_spect[down_entries[b].frame * wav_data.n_channels() + ch][d] = fft_out[down_entries[b].frame * wav_data.n_channels() + ch][d] * (mag_factor - 1);
}
}
}
}
for (int f = 0; f < Params::block * Params::frames_per_bit; f++)
{
for (int ch = 0; ch < wav_data.n_channels(); ch++)
{
/* add watermark to output frame */
vector<float> fft_delta_out = ifft (fft_delta_spect[f * wav_data.n_channels() + ch]);
vector<float> input_frame = get_frame (wav_data, f, ch);
vector<float> synth_window (Params::frame_size);
for (size_t i = 0; i < Params::frame_size; i++)
{
const double threshold = 0.2;
// triangular basic window
const double tri = min (1.0 - fabs (double (2 * i)/Params::frame_size - 1.0), threshold) / threshold;
// cosine
synth_window[i] = (cos (tri*M_PI+M_PI)+1) * 0.5;
}
vector<float> new_frame = input_frame;
for (size_t i = 0; i < new_frame.size(); i++)
new_frame[i] += fft_delta_out[i] * synth_window[i];
/* modify out signal */
for (size_t i = 0; i < new_frame.size(); i++)
out_signal[(f * Params::frame_size + i) * wav_data.n_channels() + ch] = new_frame[i] * Params::pre_scale;
}
}
}
else
{
for (int f = 0; f < frame_count (wav_data); f++)
{
for (int ch = 0; ch < wav_data.n_channels(); ch++)
......@@ -466,6 +593,7 @@ add_watermark (const string& infile, const string& outfile, const string& bits)
out_signal[(f_shuffle * Params::frame_size + i) * wav_data.n_channels() + ch] = new_frame[i] * Params::pre_scale;
}
}
}
bool clipping_warning = false;
for (auto value : out_signal)
......@@ -497,6 +625,84 @@ get_watermark (const string& infile, const string& orig_pattern)
}
vector<int> bit_vec;
if (Params::mix)
{
struct MixEntry {
int frame;
int band;
};
vector<MixEntry> up_entries, down_entries;
for (int f = 0; f < Params::block * Params::frames_per_bit; f++)
{
vector<int> up;
vector<int> down;
get_up_down (f, up, down);
for (auto u : up)
up_entries.push_back ({ f, u });
for (auto d : down)
down_entries.push_back ({ f, d });
}
gen_shuffle2 (up_entries, /* seed */ 0);
gen_shuffle2 (down_entries, /* seed */ 1);
vector<vector<complex<float>>> fft_out;
for (int f = 0; f < Params::block * Params::frames_per_bit; f++)
{
for (int ch = 0; ch < wav_data.n_channels(); ch++)
{
vector<float> frame = get_frame (wav_data, f, ch);
/* windowing */
double window_weight = 0;
for (size_t i = 0; i < frame.size(); i++)
{
const double fsize_2 = frame.size() / 2.0;
// const double win = window_cos ((i - fsize_2) / fsize_2);
const double win = window_hamming ((i - fsize_2) / fsize_2);
//const double win = 1;
frame[i] *= win;
window_weight += win;
}
/* to get normalized fft output corrected by window weight */
for (size_t i = 0; i < frame.size(); i++)
frame[i] *= 2.0 / window_weight;
/* FFT transform */
fft_out.push_back (fft (frame));
}
}
double umag = 0, dmag = 0;
for (int f = 0; f < Params::block * Params::frames_per_bit; f++)
{
for (int ch = 0; ch < wav_data.n_channels(); ch++)
{
for (int frame_b = 0; frame_b < Params::bands_per_frame; frame_b++)
{
int b = f * Params::bands_per_frame + frame_b;
const double min_db = -96;
const int u = up_entries[b].band;
{
umag += db_from_factor (abs (fft_out[up_entries[b].frame * wav_data.n_channels() + ch][u]), min_db);
}
const int d = down_entries[b].band;
{
dmag += db_from_factor (abs (fft_out[down_entries[b].frame * wav_data.n_channels() + ch][d]), min_db);
}
}
}
if ((f % Params::frames_per_bit) == (Params::frames_per_bit - 1))
{
bit_vec.push_back ((umag > dmag) ? 1 : 0);
umag = 0;
dmag = 0;
}
}
}
else
{
double umag = 0, dmag = 0;
for (int f = 0; f < frame_count (wav_data); f++)
{
......@@ -548,6 +754,7 @@ get_watermark (const string& infile, const string& orig_pattern)
dmag = 0;
}
}
}
printf ("pattern %s\n", bit_vec_to_str (bit_vec).c_str());
if (!orig_pattern.empty())
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment