Commit ce4fefab authored by Stefan Westerfeld's avatar Stefan Westerfeld

Merge branch 'multi-key-decode'

* multi-key-decode:
  Remove blank line in wmspeed.cc.
  BlockDecoder: use mix_or_linear_decode from ClipDecoder as well
  Change result order for same time: AB blocks should go after B block
  SyncFinder: merge init_up_down and get_sync_bits
  README.adoc: document key names and audiowmark get using multiple keys
  Refactor: implement parse_key by using parse_key_list.
  Use checks during string to number conversions to avoid invalid input.
  Set permissions on newly generated keys: only allow user to read/write key.
  WavData: reserve memory if possible before loading samples (performance).
  For results with the same time, sort by bits to get deterministic output.
  Print key names and speeds properly in default output.
  Deal with special chars that need escaping for gen-key --name.
  Generate properly escaped JSON string for key names in JSON output.
  Include speed in json output matches.
  Print out key names in json output.
  Key: support loading key names from key file.
  Support gen-key --name [ <key_name> ] option, store name in key file.
  Fix unused lambda capture warning.
  SyncFinder: use thread pool to perform fft for search_approx (performance)
  Fix audiowmark cmp output for speed detection
  More speed detection cleanups.
  Refactor/cleanup speed detection code.
  Simplify speed detection vector code using push_back instead emplace_back.
  Don't return speed 1 as search result from speed detection.
  Merge key loop into the detect speed function (performance).
  Refactor speed detection API to use a key list instead of a single key.
  Use ThreadPool in ClipDecoder for convolutional decoder (performance).
  Use ThreadPool to speed up BlockDecoder convolutional pattern decoding.
  Properly sort results after parallel sync refine.
  Use ThreadPool for refining sync results (performance).
  Add name member to Key.
  Reduce frame splitting for search_approx (performance).
  Use more threads in SyncFinder::search_approx (performance).
  Use thread pool in SyncFinder search to speed up multi-key sync.
  SyncFinder: remove sync_bits from KeyResult structure.
  Move key list loop into SyncFinder/BlockDecoder/ClipDecoder.
  Avoid global state for sync / data frame positions.
  Support more than one --key / --test-key option for audiowmark get/cmp.
  Get rid of single global key for PRNG.
Signed-off-by: Stefan Westerfeld's avatarStefan Westerfeld <stefan@space.twc.de>
parents 21452170 dfaf5de7
......@@ -163,6 +163,19 @@ and can be used for the add/get commands as follows:
audiowmark add --key test.key in.wav out.wav 0123456789abcdef0011223344556677
audiowmark get --key test.key out.wav
Keys can be named using the `gen-key --name` option, and the key name will be
reported for each match:
audiowmark gen-key oct23.key --name "October 2023"
Finally, it is possible to use the `--key` option more than once for watermark
detection. In this case, all keys that are specified will be tried. This is
useful if you change keys on a regular basis, and passing multiple keys is
more efficient than performing watermark detection multiple times with one
key.
audiowmark get --key oct23.key --key nov23.key --key dec23.key out.wav
[[strength]]
== Watermark Strength
......
This diff is collapsed.
......@@ -47,7 +47,7 @@ hls_prepare (const string& in_dir, const string& out_dir, const string& filename
}
int
hls_add (const string& infile, const string& outfile, const string& bits)
hls_add (const Key& key, const string& infile, const string& outfile, const string& bits)
{
error ("audiowmark: hls support is not available in this build of audiowmark\n");
return 1;
......@@ -201,7 +201,7 @@ ff_decode (const string& filename, WavData& out_wav_data)
}
int
hls_add (const string& infile, const string& outfile, const string& bits)
hls_add (const Key& key, const string& infile, const string& outfile, const string& bits)
{
TSReader reader;
......@@ -276,7 +276,7 @@ hls_add (const string& infile, const string& outfile, const string& bits)
return 1;
}
int wm_rc = add_stream_watermark (&in_stream, &out_stream, bits, start_pos - prev_size);
int wm_rc = add_stream_watermark (key, &in_stream, &out_stream, bits, start_pos - prev_size);
if (wm_rc != 0)
return wm_rc;
......
......@@ -20,7 +20,7 @@
#include <string>
int hls_add (const std::string& infile, const std::string& outfile, const std::string& bits);
int hls_add (const Key& key, const std::string& infile, const std::string& outfile, const std::string& bits);
int hls_prepare (const std::string& in_dir, const std::string& out_dir, const std::string& filename, const std::string& audio_master);
Error ff_decode (const std::string& filename, WavData& out_wav_data);
......
......@@ -21,6 +21,7 @@
#include <regex>
#include <assert.h>
#include <cinttypes>
using std::string;
using std::vector;
......@@ -52,7 +53,6 @@ gcrypt_init()
}
static vector<unsigned char> aes_key (16); // 128 bits
static constexpr auto GCRY_CIPHER = GCRY_CIPHER_AES128;
static void
......@@ -95,20 +95,20 @@ print (const string& label, const vector<unsigned char>& data)
}
#endif
Random::Random (uint64_t start_seed, Stream stream)
Random::Random (const Key& key, uint64_t start_seed, Stream stream)
{
gcrypt_init();
gcry_error_t gcry_ret = gcry_cipher_open (&aes_ctr_cipher, GCRY_CIPHER, GCRY_CIPHER_MODE_CTR, 0);
die_on_error ("gcry_cipher_open", gcry_ret);
gcry_ret = gcry_cipher_setkey (aes_ctr_cipher, &aes_key[0], aes_key.size());
gcry_ret = gcry_cipher_setkey (aes_ctr_cipher, key.aes_key(), Key::SIZE);
die_on_error ("gcry_cipher_setkey", gcry_ret);
gcry_ret = gcry_cipher_open (&seed_cipher, GCRY_CIPHER, GCRY_CIPHER_MODE_ECB, 0);
die_on_error ("gcry_cipher_open", gcry_ret);
gcry_ret = gcry_cipher_setkey (seed_cipher, &aes_key[0], aes_key.size());
gcry_ret = gcry_cipher_setkey (seed_cipher, key.aes_key(), Key::SIZE);
die_on_error ("gcry_cipher_setkey", gcry_ret);
seed (start_seed, stream);
......@@ -120,19 +120,19 @@ Random::seed (uint64_t seed, Stream stream)
buffer_pos = 0;
buffer.clear();
unsigned char plain_text[aes_key.size()];
unsigned char cipher_text[aes_key.size()];
unsigned char plain_text[Key::SIZE];
unsigned char cipher_text[Key::SIZE];
memset (plain_text, 0, sizeof (plain_text));
uint64_to_buffer (seed, &plain_text[0]);
plain_text[8] = uint8_t (stream);
gcry_error_t gcry_ret = gcry_cipher_encrypt (seed_cipher, &cipher_text[0], aes_key.size(),
&plain_text[0], aes_key.size());
gcry_error_t gcry_ret = gcry_cipher_encrypt (seed_cipher, &cipher_text[0], Key::SIZE,
&plain_text[0], Key::SIZE);
die_on_error ("gcry_cipher_encrypt", gcry_ret);
gcry_ret = gcry_cipher_setctr (aes_ctr_cipher, &cipher_text[0], aes_key.size());
gcry_ret = gcry_cipher_setctr (aes_ctr_cipher, &cipher_text[0], Key::SIZE);
die_on_error ("gcry_cipher_setctr", gcry_ret);
}
......@@ -172,14 +172,129 @@ Random::die_on_error (const char *func, gcry_error_t err)
}
}
string
Random::gen_key()
{
gcrypt_init();
vector<unsigned char> key (16);
gcry_randomize (&key[0], 16, /* long term key material strength */ GCRY_VERY_STRONG_RANDOM);
return vec_to_hex_str (key);
}
uint64_t
Random::seed_from_hash (const vector<float>& floats)
{
unsigned char hash[20];
gcry_md_hash_buffer (GCRY_MD_SHA1, hash, &floats[0], floats.size() * sizeof (float));
return uint64_from_buffer (hash);
}
Key::Key() :
m_aes_key (SIZE)
{
}
Key::~Key()
{
std::fill (m_aes_key.begin(), m_aes_key.end(), 0);
}
void
Random::set_global_test_key (uint64_t key)
Key::set_test_key (uint64_t key)
{
uint64_to_buffer (key, m_aes_key.data());
m_name = string_printf ("test-key-%" PRId64, key);
}
static bool
string_chars (char ch)
{
if ((ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9')
|| (ch >= 'a' && ch <= 'z')
|| (ch == '.')
|| (ch == ':')
|| (ch == '=')
|| (ch == '/')
|| (ch == '-')
|| (ch == '_'))
return true;
return false;
}
static bool
white_space (char ch)
{
uint64_to_buffer (key, &aes_key[0]);
return (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r');
}
static bool
tokenize (const string& line, vector<string>& tokens)
{
enum { BLANK, STRING, QUOTED_STRING, QUOTED_STRING_ESCAPED, COMMENT } state = BLANK;
string s;
string xline = line + '\n';
tokens.clear();
for (string::const_iterator i = xline.begin(); i != xline.end(); i++)
{
if (state == BLANK && string_chars (*i))
{
state = STRING;
s += *i;
}
else if (state == BLANK && *i == '"')
{
state = QUOTED_STRING;
}
else if (state == BLANK && white_space (*i))
{
// ignore more whitespaces if we've already seen one
}
else if (state == STRING && string_chars (*i))
{
s += *i;
}
else if ((state == STRING && white_space (*i))
|| (state == QUOTED_STRING && *i == '"'))
{
tokens.push_back (s);
s = "";
state = BLANK;
}
else if (state == QUOTED_STRING && *i == '\\')
{
state = QUOTED_STRING_ESCAPED;
}
else if (state == QUOTED_STRING)
{
s += *i;
}
else if (state == QUOTED_STRING_ESCAPED)
{
s += *i;
state = QUOTED_STRING;
}
else if (*i == '#')
{
state = COMMENT;
}
else if (state == COMMENT)
{
// ignore comments
}
else
{
return false;
}
}
return state == BLANK || state == COMMENT;
}
void
Random::load_global_key (const string& key_file)
Key::load_key (const string& key_file)
{
FILE *f = fopen (key_file.c_str(), "r");
if (!f)
......@@ -187,35 +302,44 @@ Random::load_global_key (const string& key_file)
error ("audiowmark: error opening key file: '%s'\n", key_file.c_str());
exit (1);
}
const regex blank_re (R"(\s*(#.*)?[\r\n]+)");
const regex key_re (R"(\s*key\s+([0-9a-f]+)\s*(#.*)?[\r\n]+)");
m_name = key_file;
// basename
size_t sep = m_name.find_last_of ("\\/");
if (sep != string::npos)
m_name = m_name.substr (sep + 1);
char buffer[1024];
int line = 1;
int keys = 0;
while (fgets (buffer, 1024, f))
{
string s = buffer;
std::smatch match;
if (regex_match (s, blank_re))
{
/* blank line or comment */
}
else if (regex_match (s, match, key_re))
vector<string> tokens;
bool parse_ok = false;
if (tokenize (buffer, tokens))
{
/* line containing aes key */
vector<unsigned char> key = hex_str_to_vec (match[1].str());
if (key.size() != aes_key.size())
if (tokens.size() == 2 && tokens[0] == "key") /* line containing aes key */
{
vector<unsigned char> key = hex_str_to_vec (tokens[1]);
if (key.size() != Key::SIZE)
{
error ("audiowmark: wrong key length in key file '%s', line %d\n => required key length is %zd bits\n", key_file.c_str(), line, Key::SIZE * 8);
exit (1);
}
m_aes_key = key;
keys++;
parse_ok = true;
}
if (tokens.size() == 2 && tokens[0] == "name") /* key name */
{
error ("audiowmark: wrong key length in key file '%s', line %d\n => required key length is %zd bits\n", key_file.c_str(), line, aes_key.size() * 8);
exit (1);
m_name = tokens[1];
parse_ok = true;
}
if (tokens.empty()) /* blank line or comment */
{
parse_ok = true;
}
aes_key = key;
keys++;
}
else
if (!parse_ok)
{
error ("audiowmark: parse error in key file '%s', line %d\n", key_file.c_str(), line);
exit (1);
......@@ -236,20 +360,15 @@ Random::load_global_key (const string& key_file)
}
}
string
Random::gen_key()
const unsigned char *
Key::aes_key() const
{
gcrypt_init();
vector<unsigned char> key (16);
gcry_randomize (&key[0], 16, /* long term key material strength */ GCRY_VERY_STRONG_RANDOM);
return vec_to_hex_str (key);
assert (m_aes_key.size() == SIZE);
return m_aes_key.data();
}
uint64_t
Random::seed_from_hash (const vector<float>& floats)
const string&
Key::name() const
{
unsigned char hash[20];
gcry_md_hash_buffer (GCRY_MD_SHA1, hash, &floats[0], floats.size() * sizeof (float));
return uint64_from_buffer (hash);
return m_name;
}
......@@ -25,6 +25,22 @@
#include <string>
#include <random>
class Key
{
std::vector<unsigned char> m_aes_key;
std::string m_name;
public:
static constexpr size_t SIZE = 16; /* 128 bits */
Key();
~Key();
void set_test_key (uint64_t key);
void load_key (const std::string& filename);
const unsigned char *aes_key() const;
const std::string& name() const;
};
class Random
{
public:
......@@ -46,7 +62,7 @@ private:
void die_on_error (const char *func, gcry_error_t error);
public:
Random (uint64_t seed, Stream stream);
Random (const Key& key, uint64_t seed, Stream stream);
~Random();
typedef uint64_t result_type;
......@@ -90,8 +106,6 @@ public:
}
}
static void set_global_test_key (uint64_t seed);
static void load_global_key (const std::string& key_file);
static std::string gen_key();
static uint64_t seed_from_hash (const std::vector<float>& floats);
};
......
This diff is collapsed.
......@@ -20,6 +20,8 @@
#include "convcode.hh"
#include "wavdata.hh"
#include "random.hh"
#include "threadpool.hh"
/*
* The SyncFinder class searches for sync bits in an input WavData. It is used
......@@ -77,31 +79,39 @@ public:
std::vector<int> up;
std::vector<int> down;
};
struct KeyResult
{
Key key;
std::vector<Score> sync_scores;
};
private:
std::vector<std::vector<FrameBit>> sync_bits;
void init_up_down (const WavData& wav_data, Mode mode);
double sync_decode (const WavData& wav_data, const size_t start_frame,
double sync_decode (const std::vector<std::vector<FrameBit>>& sync_bits,
const WavData& wav_data, const size_t start_frame,
const std::vector<float>& fft_out_db,
const std::vector<char>& have_frames,
ConvBlockType *block_type);
void scan_silence (const WavData& wav_data);
std::vector<Score> search_approx (const WavData& wav_data, Mode mode);
void search_approx (std::vector<KeyResult>& key_results, const std::vector<std::vector<std::vector<FrameBit>>>& sync_bits, const WavData& wav_data, Mode mode);
void sync_select_by_threshold (std::vector<Score>& sync_scores);
void sync_select_n_best (std::vector<Score>& sync_scores, size_t n);
void search_refine (const WavData& wav_data, Mode mode, std::vector<Score>& sync_scores);
std::vector<Score> fake_sync (const WavData& wav_data, Mode mode);
void search_refine (const WavData& wav_data, Mode mode, KeyResult& key_result, const std::vector<std::vector<FrameBit>>& sync_bits);
std::vector<KeyResult> fake_sync (const std::vector<Key>& key_list, const WavData& wav_data, Mode mode);
// non-zero sample range: [wav_data_first, wav_data_last)
size_t wav_data_first = 0;
size_t wav_data_last = 0;
public:
std::vector<Score> search (const WavData& wav_data, Mode mode);
std::vector<std::vector<FrameBit>> get_sync_bits (const WavData& wav_data, Mode mode);
std::vector<KeyResult> search (const std::vector<Key>& key_list, const WavData& wav_data, Mode mode);
static std::vector<std::vector<FrameBit>> get_sync_bits (const Key& key, const WavData& wav_data, Mode mode);
static double bit_quality (float umag, float dmag, int bit);
static double normalize_sync_quality (double raw_quality);
private:
void sync_fft_parallel (ThreadPool& thread_pool,
const WavData& wav_data,
size_t index,
std::vector<float>& fft_out_db,
std::vector<char>& have_frames);
void sync_fft (const WavData& wav_data,
size_t index,
size_t frame_count,
......@@ -109,6 +119,7 @@ private:
std::vector<char>& have_frames,
const std::vector<char>& want_frames);
std::string find_closest_sync (size_t index);
std::vector<std::vector<int>> split_vector (std::vector<int>& in_vector, size_t max_size);
};
#endif
......@@ -116,14 +116,14 @@ public:
};
int
mark_zexpand (WavData& wav_data, size_t zero_frames, const string& bits)
mark_zexpand (const Key& key, WavData& wav_data, size_t zero_frames, const string& bits)
{
WDInputStream in_stream (&wav_data);
WavData wav_data_out ({ /* no samples */ }, wav_data.n_channels(), wav_data.sample_rate(), wav_data.bit_depth());
WDOutputStream out_stream (&wav_data_out);
int rc = add_stream_watermark (&in_stream, &out_stream, bits, zero_frames);
int rc = add_stream_watermark (key, &in_stream, &out_stream, bits, zero_frames);
if (rc != 0)
return rc;
......@@ -133,7 +133,7 @@ mark_zexpand (WavData& wav_data, size_t zero_frames, const string& bits)
}
int
test_seek (const string& in, const string& out, int pos, const string& bits)
test_seek (const Key& key, const string& in, const string& out, int pos, const string& bits)
{
vector<float> samples;
WavData wav_data;
......@@ -148,7 +148,7 @@ test_seek (const string& in, const string& out, int pos, const string& bits)
samples.erase (samples.begin(), samples.begin() + pos * wav_data.n_channels());
wav_data.set_samples (samples);
int rc = mark_zexpand (wav_data, pos, bits);
int rc = mark_zexpand (key, wav_data, pos, bits);
if (rc != 0)
{
return rc;
......@@ -168,14 +168,14 @@ test_seek (const string& in, const string& out, int pos, const string& bits)
}
int
seek_perf (int sample_rate, double seconds)
seek_perf (const Key& key, int sample_rate, double seconds)
{
vector<float> samples (100);
WavData wav_data (samples, 2, sample_rate, 16);
double start_time = get_time();
int rc = mark_zexpand (wav_data, seconds * sample_rate, "0c");
int rc = mark_zexpand (key, wav_data, seconds * sample_rate, "0c");
if (rc != 0)
return rc;
......@@ -191,13 +191,14 @@ seek_perf (int sample_rate, double seconds)
int
main (int argc, char **argv)
{
Key global_key;
if (argc == 6 && strcmp (argv[1], "test-seek") == 0)
{
return test_seek (argv[2], argv[3], atoi (argv[4]), argv[5]);
return test_seek (global_key, argv[2], argv[3], atoi (argv[4]), argv[5]);
}
else if (argc == 4 && strcmp (argv[1], "seek-perf") == 0)
{
return seek_perf (atoi (argv[2]), atof (argv[3]));
return seek_perf (global_key, atoi (argv[2]), atof (argv[3]));
}
else if (argc == 4 && strcmp (argv[1], "ff-decode") == 0)
{
......
......@@ -26,7 +26,8 @@ using std::string;
int
main (int argc, char **argv)
{
Random rng (0xf00f1234b00b5678U, Random::Stream::bit_order);
Key key;
Random rng (key, 0xf00f1234b00b5678U, Random::Stream::bit_order);
for (size_t i = 0; i < 20; i++)
{
uint64_t x = rng();
......
......@@ -57,6 +57,9 @@ WavData::load (AudioInputStream *in_stream)
{
m_samples.clear(); // get rid of old contents
if (in_stream->n_frames() != AudioInputStream::N_FRAMES_UNKNOWN)
m_samples.reserve (in_stream->n_frames() * in_stream->n_channels());
vector<float> m_buffer;
while (true)
{
......
......@@ -83,7 +83,7 @@ apply_frame_mod (const vector<FrameMod>& frame_mod, const vector<complex<float>>
}
static void
mark_data (vector<vector<FrameMod>>& frame_mod, const vector<int>& bitvec)
mark_data (const Key& key, vector<vector<FrameMod>>& frame_mod, const vector<int>& bitvec)
{
assert (bitvec.size() == mark_data_frame_count() / Params::frames_per_bit);
assert (frame_mod.size() >= mark_data_frame_count());
......@@ -92,7 +92,7 @@ mark_data (vector<vector<FrameMod>>& frame_mod, const vector<int>& bitvec)
if (Params::mix)
{
vector<MixEntry> mix_entries = gen_mix_entries();
vector<MixEntry> mix_entries = gen_mix_entries (key);
for (int f = 0; f < frame_count; f++)
{
......@@ -113,11 +113,12 @@ mark_data (vector<vector<FrameMod>>& frame_mod, const vector<int>& bitvec)
}
else
{
UpDownGen up_down_gen (Random::Stream::data_up_down);
UpDownGen up_down_gen (key, Random::Stream::data_up_down);
BitPosGen bit_pos_gen (key);
for (int f = 0; f < frame_count; f++)
{
size_t index = data_frame_pos (f);
size_t index = bit_pos_gen.data_frame (f);
prepare_frame_mod (up_down_gen, f, frame_mod[index], bitvec[f / Params::frames_per_bit]);
}
......@@ -125,17 +126,18 @@ mark_data (vector<vector<FrameMod>>& frame_mod, const vector<int>& bitvec)
}
static void
mark_sync (vector<vector<FrameMod>>& frame_mod, int ab)
mark_sync (const Key& key, vector<vector<FrameMod>>& frame_mod, int ab)
{
const int frame_count = mark_sync_frame_count();
assert (frame_mod.size() >= mark_sync_frame_count());
UpDownGen up_down_gen (Random::Stream::sync_up_down);
UpDownGen up_down_gen (key, Random::Stream::sync_up_down);
BitPosGen bit_pos_gen (key);
// sync block always written in linear order (no mix)
for (int f = 0; f < frame_count; f++)
{
size_t index = sync_frame_pos (f);
size_t index = bit_pos_gen.sync_frame (f);
int data_bit = (f / Params::sync_frames_per_bit + ab) & 1; /* write 010101 for a block, 101010 for b block */
prepare_frame_mod (up_down_gen, f, frame_mod[index], data_bit);
......@@ -143,7 +145,7 @@ mark_sync (vector<vector<FrameMod>>& frame_mod, int ab)
}
static void
init_frame_mod_vec (vector<vector<FrameMod>>& frame_mod_vec, int ab, const vector<int>& bitvec)
init_frame_mod_vec (const Key& key, vector<vector<FrameMod>>& frame_mod_vec, int ab, const vector<int>& bitvec)
{
frame_mod_vec.resize (mark_sync_frame_count() + mark_data_frame_count());
......@@ -152,10 +154,10 @@ init_frame_mod_vec (vector<vector<FrameMod>>& frame_mod_vec, int ab, const vecto
/* forward error correction */
ConvBlockType block_type = ab ? ConvBlockType::b : ConvBlockType::a;
vector<int> bitvec_fec = randomize_bit_order (code_encode (block_type, bitvec), /* encode */ true);
vector<int> bitvec_fec = randomize_bit_order (key, code_encode (block_type, bitvec), /* encode */ true);
mark_sync (frame_mod_vec, ab);
mark_data (frame_mod_vec, bitvec_fec);
mark_sync (key, frame_mod_vec, ab);
mark_data (key, frame_mod_vec, bitvec_fec);
}
/* synthesizes a watermark stream (overlap add with synthesis window)
......@@ -292,7 +294,7 @@ public:
frame_number = 2 * frames_per_block - Params::frames_pad_start;
}
vector<float>
run (const vector<float>& samples)
run (const Key& key, const vector<float>& samples)
{
assert (samples.size() == Params::frame_size * n_channels);
......@@ -302,7 +304,7 @@ public:
for (int ch = 0; ch < n_channels; ch++)
fft_delta_spect.push_back (vector<complex<float>> (fft_out.back().size()));
const vector<FrameMod>& frame_mod = get_frame_mod();
const vector<FrameMod>& frame_mod = get_frame_mod (key);
for (int ch = 0; ch < n_channels; ch++)
apply_frame_mod (frame_mod, fft_out[ch], fft_delta_spect[ch]);
......@@ -321,20 +323,20 @@ public:
return wm_synth.skip (zeros);
}
const vector<FrameMod>&
get_frame_mod()
get_frame_mod (const Key& key)
{
const size_t f = frame_number % (frames_per_block * 2);
if (f >= frames_per_block) /* B block */
{
if (frame_mod_vec_b.empty())
init_frame_mod_vec (frame_mod_vec_b, 1, bitvec);
init_frame_mod_vec (key, frame_mod_vec_b, 1, bitvec);
return frame_mod_vec_b[f - frames_per_block];
}
else /* A block */
{
if (frame_mod_vec_a.empty())
init_frame_mod_vec (frame_mod_vec_a, 0, bitvec);
init_frame_mod_vec (key, frame_mod_vec_a, 0, bitvec);
return frame_mod_vec_a[f];
}
......@@ -529,12 +531,12 @@ public:
return true;
}
vector<float>
run (const vector<float>& samples)
run (const Key& key, const vector<float>& samples)
{
if (!need_resampler)
{
/* cheap case: if no resampling is necessary, just generate the watermark signal */
return wm_gen.run (samples);
return wm_gen.run (key, samples);
}
/* resample to the watermark sample rate */
......@@ -544,7 +546,7 @@ public:
vector<float> r_samples = in_resampler->read_frames (Params::frame_size);
/* generate watermark at normalized sample rate */
vector<float> wm_samples = wm_gen.run (r_samples);
vector<float> wm_samples = wm_gen.run (key, r_samples);
/* resample back to the original sample rate of the audio file */
out_resampler->write_frames (wm_samples);
......@@ -588,7 +590,7 @@ info_format (const string& label, const RawFormat& format)
}
int
add_stream_watermark (AudioInputStream *in_stream, AudioOutputStream *out_stream, const string& bits, size_t zero_frames)
add_stream_watermark (const Key& key, AudioInputStream *in_stream, AudioOutputStream *out_stream, const string& bits, size_t zero_frames)
{
auto bitvec = parse_payload (bits);
if (bitvec.empty())
......@@ -687,7 +689,7 @@ add_stream_watermark (AudioInputStream *in_stream, AudioOutputStream *out_stream
samples.resize (Params::frame_size * n_channels);
}
audio_buffer.write_frames (samples);
samples = wm_resampler.run (samples);
samples = wm_resampler.run (key, samples);
size_t to_read = samples.size() / n_channels;
vector<float> orig_samples = audio_buffer.read_frames (to_read);
assert (samples.size() == orig_samples.size());
......@@ -760,7 +762,7 @@ add_stream_watermark (AudioInputStream *in_stream, AudioOutputStream *out_stream
}
int
add_watermark (const string& infile, const string& outfile, const string& bits)
add_watermark (const Key& key, const string& infile, const string& outfile, const string& bits)
{
/* open input stream */
Error err;
......@@ -789,7 +791,7 @@ add_watermark (const string& infile, const string& outfile, const string& bits)
if (Params::output_format == Format::RAW)
info_format ("Raw Output", Params::raw_output_format);
return add_stream_watermark (in_stream.get(), out_stream.get(), bits, 0);
return add_stream_watermark (key, in_stream.get(), out_stream.get(), bits, 0);
}
......@@ -34,7 +34,6 @@ bool Params::detect_speed = false;
bool Params::detect_speed_patient = false;
double Params::try_speed = -1;
double Params::test_speed = -1;
int Params::have_key = 0;
size_t Params::payload_size = 128;
bool Params::payload_short = false;
int Params::test_cut = 0; // for sync test
......@@ -140,44 +139,28 @@ FFTAnalyzer::fft_range (const vector<float>& samples, size_t start_index, size_t
return fft_out;
}
int
frame_pos (int f, bool sync)
BitPosGen::BitPosGen (const Key& key)
{
static vector<int> pos_vec;
if (pos_vec.empty())
{
int frame_count = mark_data_frame_count() + mark_sync_frame_count();
for (int i = 0; i < frame_count; i++)
pos_vec.push_back (i);
Random random (0, Random::Stream::frame_position);
random.shuffle (pos_vec);
}
if (sync)
{
assert (f >= 0 && size_t (f) < mark_sync_frame_count());
int frame_count = mark_data_frame_count() + mark_sync_frame_count();
for (int i = 0; i < frame_count; i++)
pos_vec.push_back (i);
return pos_vec[f];
}
else
{
assert (f >= 0 && size_t (f) < mark_data_frame_count());
return pos_vec[f + mark_sync_frame_count()];
}
Random random (key, 0, Random::Stream::frame_position);
random.shuffle (pos_vec);
}
int
sync_frame_pos (int f)
BitPosGen::sync_frame (int f)
{
return frame_pos (f, true);
assert (f >= 0 && size_t (f) < mark_sync_frame_count());
return pos_vec[f];
}
int
data_frame_pos (int f)
BitPosGen::data_frame (int f)
{
return frame_pos (f, false);
assert (f >= 0 && size_t (f) < mark_data_frame_count());
return pos_vec[f + mark_sync_frame_count()];
}
size_t
......@@ -193,16 +176,17 @@ mark_sync_frame_count()
}
vector<MixEntry>
gen_mix_entries()
gen_mix_entries (const Key& key)
{
const int frame_count = mark_data_frame_count();
vector<MixEntry> mix_entries (frame_count * Params::bands_per_frame);
UpDownGen up_down_gen (Random::Stream::data_up_down);
UpDownGen up_down_gen (key, Random::Stream::data_up_down);
BitPosGen bit_pos_gen (key);
int entry = 0;
for (int f = 0; f < frame_count; f++)
{
const int index = data_frame_pos (f);
const int index = bit_pos_gen.data_frame (f);
UpDownArray up, down;
up_down_gen.get (f, up, down);
......@@ -210,7 +194,7 @@ gen_mix_entries()
for (size_t i = 0; i < up.size(); i++)
mix_entries[entry++] = { index, up[i], down[i] };
}
Random random (/* seed */ 0, Random::Stream::mix);
Random random (key, /* seed */ 0, Random::Stream::mix);
random.shuffle (mix_entries);
return mix_entries;
......
......@@ -45,7 +45,6 @@ public:
static bool mix;
static bool hard; // hard decode bits? (soft decoding is better)
static bool snr; // compute/show snr while adding watermark
static int have_key;
static bool detect_speed;
static bool detect_speed_patient;
......@@ -94,9 +93,9 @@ class UpDownGen
std::vector<int> bands_reorder;
public:
UpDownGen (Random::Stream random_stream) :
UpDownGen (const Key& key, Random::Stream random_stream) :
random_stream (random_stream),
random (0, random_stream),
random (key, 0, random_stream),
bands_reorder (Params::max_band - Params::min_band + 1)
{
UpDownArray x;
......@@ -120,6 +119,16 @@ public:
}
};
class BitPosGen
{
std::vector<int> pos_vec;
public:
BitPosGen (const Key& key);
int sync_frame (int f);
int data_frame (int f);
};
class FFTAnalyzer
{
int m_n_channels = 0;
......@@ -141,27 +150,24 @@ struct MixEntry
int down;
};
std::vector<MixEntry> gen_mix_entries();
std::vector<MixEntry> gen_mix_entries (const Key& key);
size_t mark_data_frame_count();
size_t mark_sync_frame_count();
int frame_count (const WavData& wav_data);
int sync_frame_pos (int f);
int data_frame_pos (int f);
std::vector<int> parse_payload (const std::string& str);
template<class T> std::vector<T>
randomize_bit_order (const std::vector<T>& bit_vec, bool encode)
randomize_bit_order (const Key& key, const std::vector<T>& bit_vec, bool encode)
{
std::vector<unsigned int> order;
for (size_t i = 0; i < bit_vec.size(); i++)
order.push_back (i);
Random random (/* seed */ 0, Random::Stream::bit_order);
Random random (key, /* seed */ 0, Random::Stream::bit_order);
random.shuffle (order);
std::vector<T> out_bits (bit_vec.size());
......@@ -214,8 +220,8 @@ db_from_complex (std::complex<float> f, float min_dB)
return db_from_complex (f.real(), f.imag(), min_dB);
}
int add_stream_watermark (AudioInputStream *in_stream, AudioOutputStream *out_stream, const std::string& bits, size_t zero_frames);
int add_watermark (const std::string& infile, const std::string& outfile, const std::string& bits);
int get_watermark (const std::string& infile, const std::string& orig_pattern);
int add_stream_watermark (const Key& key, AudioInputStream *in_stream, AudioOutputStream *out_stream, const std::string& bits, size_t zero_frames);
int add_watermark (const Key& key, const std::string& infile, const std::string& outfile, const std::string& bits);
int get_watermark (const std::vector<Key>& key_list, const std::string& infile, const std::string& orig_pattern);
#endif /* AUDIOWMARK_WM_COMMON_HH */
This diff is collapsed.
This diff is collapsed.
......@@ -19,7 +19,14 @@
#define AUDIOWMARK_WM_SPEED_HH
#include "wavdata.hh"
#include "random.hh"
double detect_speed (const WavData& in_data, bool print_results);
struct DetectSpeedResult
{
Key key;
double speed = 0;
};
std::vector<DetectSpeedResult> detect_speed (const std::vector<Key>& key_list, const WavData& in_data, bool print_results);
#endif /* AUDIOWMARK_WM_SPEED_HH */
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