wmget.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /*
  2. * Copyright (C) 2018-2020 Stefan Westerfeld
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <string>
  18. #include <algorithm>
  19. #include "wavdata.hh"
  20. #include "wmcommon.hh"
  21. #include "wmspeed.hh"
  22. #include "convcode.hh"
  23. #include "shortcode.hh"
  24. #include "syncfinder.hh"
  25. #include "resample.hh"
  26. #include "fft.hh"
  27. using std::string;
  28. using std::vector;
  29. using std::min;
  30. using std::max;
  31. using std::complex;
  32. static vector<float>
  33. normalize_soft_bits (const vector<float>& soft_bits)
  34. {
  35. vector<float> norm_soft_bits;
  36. /* soft decoding produces better error correction than hard decoding */
  37. if (Params::hard)
  38. {
  39. for (auto value : soft_bits)
  40. norm_soft_bits.push_back (value > 0 ? 1.0 : 0.0);
  41. }
  42. else
  43. {
  44. /* figure out average level of each bit */
  45. double mean = 0;
  46. for (auto value : soft_bits)
  47. mean += fabs (value);
  48. mean /= soft_bits.size();
  49. /* rescale from [-mean,+mean] to [0.0,1.0] */
  50. for (auto value : soft_bits)
  51. norm_soft_bits.push_back (0.5 * (value / mean + 1));
  52. }
  53. return norm_soft_bits;
  54. }
  55. static vector<float>
  56. mix_decode (vector<vector<complex<float>>>& fft_out, int n_channels)
  57. {
  58. vector<float> raw_bit_vec;
  59. const int frame_count = mark_data_frame_count();
  60. vector<MixEntry> mix_entries = gen_mix_entries();
  61. double umag = 0, dmag = 0;
  62. for (int f = 0; f < frame_count; f++)
  63. {
  64. for (int ch = 0; ch < n_channels; ch++)
  65. {
  66. for (size_t frame_b = 0; frame_b < Params::bands_per_frame; frame_b++)
  67. {
  68. int b = f * Params::bands_per_frame + frame_b;
  69. const double min_db = -96;
  70. const size_t index = mix_entries[b].frame * n_channels + ch;
  71. const int u = mix_entries[b].up;
  72. const int d = mix_entries[b].down;
  73. umag += db_from_factor (abs (fft_out[index][u]), min_db);
  74. dmag += db_from_factor (abs (fft_out[index][d]), min_db);
  75. }
  76. }
  77. if ((f % Params::frames_per_bit) == (Params::frames_per_bit - 1))
  78. {
  79. raw_bit_vec.push_back (umag - dmag);
  80. umag = 0;
  81. dmag = 0;
  82. }
  83. }
  84. return raw_bit_vec;
  85. }
  86. static vector<float>
  87. linear_decode (vector<vector<complex<float>>>& fft_out, int n_channels)
  88. {
  89. UpDownGen up_down_gen (Random::Stream::data_up_down);
  90. vector<float> raw_bit_vec;
  91. const int frame_count = mark_data_frame_count();
  92. double umag = 0, dmag = 0;
  93. for (int f = 0; f < frame_count; f++)
  94. {
  95. for (int ch = 0; ch < n_channels; ch++)
  96. {
  97. const size_t index = data_frame_pos (f) * n_channels + ch;
  98. UpDownArray up, down;
  99. up_down_gen.get (f, up, down);
  100. const double min_db = -96;
  101. for (auto u : up)
  102. umag += db_from_factor (abs (fft_out[index][u]), min_db);
  103. for (auto d : down)
  104. dmag += db_from_factor (abs (fft_out[index][d]), min_db);
  105. }
  106. if ((f % Params::frames_per_bit) == (Params::frames_per_bit - 1))
  107. {
  108. raw_bit_vec.push_back (umag - dmag);
  109. umag = 0;
  110. dmag = 0;
  111. }
  112. }
  113. return raw_bit_vec;
  114. }
  115. class ResultSet
  116. {
  117. public:
  118. enum class Type { BLOCK, CLIP, ALL };
  119. struct Pattern
  120. {
  121. vector<int> bit_vec;
  122. float decode_error = 0;
  123. SyncFinder::Score sync_score;
  124. Type type;
  125. bool speed_pattern;
  126. };
  127. private:
  128. vector<Pattern> patterns;
  129. bool speed_pattern = false;
  130. public:
  131. void
  132. set_speed_pattern (bool sp)
  133. {
  134. speed_pattern = sp;
  135. }
  136. void
  137. add_pattern (SyncFinder::Score sync_score, const vector<int>& bit_vec, float decode_error, Type pattern_type)
  138. {
  139. Pattern p;
  140. p.sync_score = sync_score;
  141. p.bit_vec = bit_vec;
  142. p.decode_error = decode_error;
  143. p.type = pattern_type;
  144. p.speed_pattern = speed_pattern;
  145. patterns.push_back (p);
  146. }
  147. void
  148. print()
  149. {
  150. std::stable_sort (patterns.begin(), patterns.end(), [](const Pattern& p1, const Pattern& p2) {
  151. const int all1 = p1.type == Type::ALL;
  152. const int all2 = p2.type == Type::ALL;
  153. if (all1 != all2)
  154. return all1 < all2;
  155. else
  156. return p1.sync_score.index < p2.sync_score.index;
  157. });
  158. for (const auto& pattern : patterns)
  159. {
  160. if (pattern.type == Type::ALL) /* this is the combined pattern "all" */
  161. {
  162. const char *extra = "";
  163. if (pattern.speed_pattern)
  164. extra = " SPEED";
  165. printf ("pattern all %s %.3f %.3f%s\n", bit_vec_to_str (pattern.bit_vec).c_str(),
  166. pattern.sync_score.quality, pattern.decode_error,
  167. extra);
  168. }
  169. else
  170. {
  171. string block_str;
  172. switch (pattern.sync_score.block_type)
  173. {
  174. case ConvBlockType::a: block_str = "A";
  175. break;
  176. case ConvBlockType::b: block_str = "B";
  177. break;
  178. case ConvBlockType::ab: block_str = "AB";
  179. break;
  180. }
  181. if (pattern.type == Type::CLIP)
  182. block_str = "CLIP-" + block_str;
  183. if (pattern.speed_pattern)
  184. block_str += "-SPEED";
  185. const int seconds = pattern.sync_score.index / Params::mark_sample_rate;
  186. printf ("pattern %2d:%02d %s %.3f %.3f %s\n", seconds / 60, seconds % 60, bit_vec_to_str (pattern.bit_vec).c_str(),
  187. pattern.sync_score.quality, pattern.decode_error, block_str.c_str());
  188. }
  189. }
  190. }
  191. int
  192. print_match_count (const string& orig_pattern)
  193. {
  194. int match_count = 0;
  195. vector<int> orig_vec = bit_str_to_vec (orig_pattern);
  196. for (auto p : patterns)
  197. {
  198. bool match = true;
  199. for (size_t i = 0; i < p.bit_vec.size(); i++)
  200. match = match && (p.bit_vec[i] == orig_vec[i % orig_vec.size()]);
  201. if (match)
  202. match_count++;
  203. }
  204. printf ("match_count %d %zd\n", match_count, patterns.size());
  205. return match_count;
  206. }
  207. double
  208. best_quality() const
  209. {
  210. double q = -1;
  211. for (const auto& pattern : patterns)
  212. if (pattern.sync_score.quality > q)
  213. q = pattern.sync_score.quality;
  214. return q;
  215. }
  216. };
  217. /*
  218. * The block decoder is responsible for finding whole data blocks inside the
  219. * input file and decoding them. This only works for files that are large
  220. * enough to contain one data block. Incomplete blocks are ignored.
  221. *
  222. * INPUT: AA|BBBBB|AAAAA|BBB
  223. * MATCH BBBBB
  224. * MATCH AAAAA
  225. *
  226. * The basic algorithm is this:
  227. *
  228. * - use sync finder to find start index for the blocks
  229. * - decode the blocks
  230. * - try to combine A + B blocks for better error correction (AB)
  231. * - try to combine all available blocks for better error correction (all pattern)
  232. */
  233. class BlockDecoder
  234. {
  235. int debug_sync_frame_count = 0;
  236. vector<SyncFinder::Score> sync_scores; // stored here for sync debugging
  237. public:
  238. void
  239. run (const WavData& wav_data, ResultSet& result_set)
  240. {
  241. int total_count = 0;
  242. SyncFinder sync_finder;
  243. sync_scores = sync_finder.search (wav_data, SyncFinder::Mode::BLOCK);
  244. vector<float> raw_bit_vec_all (code_size (ConvBlockType::ab, Params::payload_size));
  245. vector<int> raw_bit_vec_norm (2);
  246. SyncFinder::Score score_all { 0, 0 };
  247. SyncFinder::Score score_ab { 0, 0, ConvBlockType::ab };
  248. ConvBlockType last_block_type = ConvBlockType::b;
  249. vector<vector<float>> ab_raw_bit_vec (2);
  250. vector<float> ab_quality (2);
  251. FFTAnalyzer fft_analyzer (wav_data.n_channels());
  252. for (auto sync_score : sync_scores)
  253. {
  254. const size_t count = mark_sync_frame_count() + mark_data_frame_count();
  255. const size_t index = sync_score.index;
  256. const int ab = (sync_score.block_type == ConvBlockType::b); /* A -> 0, B -> 1 */
  257. auto fft_range_out = fft_analyzer.fft_range (wav_data.samples(), index, count);
  258. if (fft_range_out.size())
  259. {
  260. /* ---- retrieve bits from watermark ---- */
  261. vector<float> raw_bit_vec;
  262. if (Params::mix)
  263. {
  264. raw_bit_vec = mix_decode (fft_range_out, wav_data.n_channels());
  265. }
  266. else
  267. {
  268. raw_bit_vec = linear_decode (fft_range_out, wav_data.n_channels());
  269. }
  270. assert (raw_bit_vec.size() == code_size (ConvBlockType::a, Params::payload_size));
  271. raw_bit_vec = randomize_bit_order (raw_bit_vec, /* encode */ false);
  272. /* ---- deal with this pattern ---- */
  273. float decode_error = 0;
  274. vector<int> bit_vec = code_decode_soft (sync_score.block_type, normalize_soft_bits (raw_bit_vec), &decode_error);
  275. if (!bit_vec.empty())
  276. result_set.add_pattern (sync_score, bit_vec, decode_error, ResultSet::Type::BLOCK);
  277. total_count += 1;
  278. /* ---- update "all" pattern ---- */
  279. score_all.quality += sync_score.quality;
  280. for (size_t i = 0; i < raw_bit_vec.size(); i++)
  281. {
  282. raw_bit_vec_all[i * 2 + ab] += raw_bit_vec[i];
  283. }
  284. raw_bit_vec_norm[ab]++;
  285. /* ---- if last block was A & this block is B => deal with combined AB block */
  286. ab_raw_bit_vec[ab] = raw_bit_vec;
  287. ab_quality[ab] = sync_score.quality;
  288. if (last_block_type == ConvBlockType::a && sync_score.block_type == ConvBlockType::b)
  289. {
  290. /* join A and B block -> AB block */
  291. vector<float> ab_bits (raw_bit_vec.size() * 2);
  292. for (size_t i = 0; i < raw_bit_vec.size(); i++)
  293. {
  294. ab_bits[i * 2] = ab_raw_bit_vec[0][i];
  295. ab_bits[i * 2 + 1] = ab_raw_bit_vec[1][i];
  296. }
  297. vector<int> bit_vec = code_decode_soft (ConvBlockType::ab, normalize_soft_bits (ab_bits), &decode_error);
  298. if (!bit_vec.empty())
  299. {
  300. score_ab.index = sync_score.index;
  301. score_ab.quality = (ab_quality[0] + ab_quality[1]) / 2;
  302. result_set.add_pattern (score_ab, bit_vec, decode_error, ResultSet::Type::BLOCK);
  303. }
  304. }
  305. last_block_type = sync_score.block_type;
  306. }
  307. }
  308. if (total_count > 1) /* all pattern: average soft bits of all watermarks and decode */
  309. {
  310. for (size_t i = 0; i < raw_bit_vec_all.size(); i += 2)
  311. {
  312. raw_bit_vec_all[i] /= max (raw_bit_vec_norm[0], 1); /* normalize A soft bits with number of A blocks */
  313. raw_bit_vec_all[i + 1] /= max (raw_bit_vec_norm[1], 1); /* normalize B soft bits with number of B blocks */
  314. }
  315. score_all.quality /= raw_bit_vec_norm[0] + raw_bit_vec_norm[1];
  316. vector<float> soft_bit_vec = normalize_soft_bits (raw_bit_vec_all);
  317. float decode_error = 0;
  318. vector<int> bit_vec = code_decode_soft (ConvBlockType::ab, soft_bit_vec, &decode_error);
  319. if (!bit_vec.empty())
  320. result_set.add_pattern (score_all, bit_vec, decode_error, ResultSet::Type::ALL);
  321. }
  322. debug_sync_frame_count = frame_count (wav_data);
  323. }
  324. void
  325. print_debug_sync()
  326. {
  327. /* search sync markers at typical positions */
  328. const int expect0 = Params::frames_pad_start * Params::frame_size;
  329. const int expect_step = (mark_sync_frame_count() + mark_data_frame_count()) * Params::frame_size;
  330. const int expect_end = debug_sync_frame_count * Params::frame_size;
  331. int sync_match = 0;
  332. for (int expect_index = expect0; expect_index + expect_step < expect_end; expect_index += expect_step)
  333. {
  334. for (auto sync_score : sync_scores)
  335. {
  336. if (abs (int (sync_score.index + Params::test_cut) - expect_index) < Params::frame_size / 2)
  337. {
  338. sync_match++;
  339. break;
  340. }
  341. }
  342. }
  343. printf ("sync_match %d %zd\n", sync_match, sync_scores.size());
  344. }
  345. };
  346. /*
  347. * The clip decoder is responsible for decoding short clips. It is designed to
  348. * handle input sizes that are smaller than one data block. One case is that
  349. * the clip contains a partial A block (so the data could start after the start
  350. * of the A block and end before the end of the A block).
  351. *
  352. * ORIG: |AAAAA|BBBBB|AAAAA|BBBBB|
  353. * CLIP: |AAA|
  354. *
  355. * A clip could also contain the end of one block and the start of the next block,
  356. * like this:
  357. *
  358. * ORIG: |AAAAA|BBBBB|AAAAA|BBBBB|
  359. * CLIP: |A|BB|
  360. *
  361. * The basic algorithm is this:
  362. *
  363. * - zeropad |AAA| => 00000|AAA|00000
  364. * - use sync finder to find start index of one long block in the zeropadded data
  365. * - decode the bits
  366. *
  367. * For files larger than one data block, we decode twice, at the beginning and end
  368. *
  369. * INPUT AAA|BBBBB|A
  370. * CLIP #1 AAA|BB
  371. * CLIP #2 BBBB|A
  372. */
  373. class ClipDecoder
  374. {
  375. const int frames_per_block = 0;
  376. vector<float>
  377. mix_or_linear_decode (vector<vector<complex<float>>>& fft_out, int n_channels)
  378. {
  379. if (Params::mix)
  380. return mix_decode (fft_out, n_channels);
  381. else
  382. return linear_decode (fft_out, n_channels);
  383. }
  384. void
  385. run_padded (const WavData& wav_data, ResultSet& result_set, double time_offset_sec)
  386. {
  387. SyncFinder sync_finder;
  388. vector<SyncFinder::Score> sync_scores = sync_finder.search (wav_data, SyncFinder::Mode::CLIP);
  389. FFTAnalyzer fft_analyzer (wav_data.n_channels());
  390. for (auto sync_score : sync_scores)
  391. {
  392. const size_t count = mark_sync_frame_count() + mark_data_frame_count();
  393. const size_t index = sync_score.index;
  394. auto fft_range_out1 = fft_analyzer.fft_range (wav_data.samples(), index, count);
  395. auto fft_range_out2 = fft_analyzer.fft_range (wav_data.samples(), index + count * Params::frame_size, count);
  396. if (fft_range_out1.size() && fft_range_out2.size())
  397. {
  398. const auto raw_bit_vec1 = randomize_bit_order (mix_or_linear_decode (fft_range_out1, wav_data.n_channels()), /* encode */ false);
  399. const auto raw_bit_vec2 = randomize_bit_order (mix_or_linear_decode (fft_range_out2, wav_data.n_channels()), /* encode */ false);
  400. const size_t bits_per_block = raw_bit_vec1.size();
  401. vector<float> raw_bit_vec;
  402. for (size_t i = 0; i < bits_per_block; i++)
  403. {
  404. if (sync_score.block_type == ConvBlockType::a)
  405. {
  406. raw_bit_vec.push_back (raw_bit_vec1[i]);
  407. raw_bit_vec.push_back (raw_bit_vec2[i]);
  408. }
  409. else
  410. {
  411. raw_bit_vec.push_back (raw_bit_vec2[i]);
  412. raw_bit_vec.push_back (raw_bit_vec1[i]);
  413. }
  414. }
  415. float decode_error = 0;
  416. vector<int> bit_vec = code_decode_soft (ConvBlockType::ab, normalize_soft_bits (raw_bit_vec), &decode_error);
  417. if (!bit_vec.empty())
  418. {
  419. SyncFinder::Score sync_score_nopad = sync_score;
  420. sync_score_nopad.index = time_offset_sec * wav_data.sample_rate();
  421. result_set.add_pattern (sync_score_nopad, bit_vec, decode_error, ResultSet::Type::CLIP);
  422. }
  423. }
  424. }
  425. }
  426. enum class Pos { START, END };
  427. void
  428. run_block (const WavData& wav_data, ResultSet& result_set, Pos pos)
  429. {
  430. const size_t n = (frames_per_block + 5) * Params::frame_size * wav_data.n_channels();
  431. // range of samples used by clip: [first_sample, last_sample)
  432. size_t first_sample;
  433. size_t last_sample;
  434. size_t pad_samples_start = n;
  435. size_t pad_samples_end = n;
  436. if (pos == Pos::START)
  437. {
  438. first_sample = 0;
  439. last_sample = min (n, wav_data.n_values());
  440. // increase padding at start for small blocks
  441. // -> (available samples + padding) must always be one L-block
  442. if (last_sample < n)
  443. pad_samples_start += n - last_sample;
  444. }
  445. else // (pos == Pos::END)
  446. {
  447. if (wav_data.n_values() <= n)
  448. return;
  449. first_sample = wav_data.n_values() - n;
  450. last_sample = wav_data.n_values();
  451. }
  452. const double time_offset = double (first_sample) / wav_data.sample_rate() / wav_data.n_channels();
  453. vector<float> ext_samples (wav_data.samples().begin() + first_sample, wav_data.samples().begin() + last_sample);
  454. if (0)
  455. {
  456. printf ("%d: %f..%f\n", int (pos), time_offset, time_offset + double (ext_samples.size()) / wav_data.sample_rate() / wav_data.n_channels());
  457. printf ("%f< >%f\n",
  458. double (pad_samples_start) / wav_data.sample_rate() / wav_data.n_channels(),
  459. double (pad_samples_end) / wav_data.sample_rate() / wav_data.n_channels());
  460. }
  461. ext_samples.insert (ext_samples.begin(), pad_samples_start, 0);
  462. ext_samples.insert (ext_samples.end(), pad_samples_end, 0);
  463. WavData l_wav_data (ext_samples, wav_data.n_channels(), wav_data.sample_rate(), wav_data.bit_depth());
  464. run_padded (l_wav_data, result_set, time_offset);
  465. }
  466. public:
  467. ClipDecoder() :
  468. frames_per_block (mark_sync_frame_count() + mark_data_frame_count())
  469. {
  470. }
  471. void
  472. run (const WavData& wav_data, ResultSet& result_set)
  473. {
  474. const int wav_frames = wav_data.n_values() / (Params::frame_size * wav_data.n_channels());
  475. if (wav_frames < frames_per_block * 3.1) /* clip decoder is only used for small wavs */
  476. {
  477. run_block (wav_data, result_set, Pos::START);
  478. run_block (wav_data, result_set, Pos::END);
  479. }
  480. }
  481. };
  482. static int
  483. decode_and_report (const WavData& wav_data, const string& orig_pattern)
  484. {
  485. ResultSet result_set;
  486. /*
  487. * The strategy for integrating speed detection into decoding is this:
  488. * - we always (unconditionally) try to decode the watermark on the original wav data
  489. * - if detected speed is somewhat different than 1.0, we also try to decode stretched data
  490. * - we report all normal and speed results we get
  491. *
  492. * The reason to do it this way is that the detected speed may be wrong (on short clips)
  493. * and we don't want to loose a successful clip decoder match in this case.
  494. */
  495. if (Params::detect_speed)
  496. {
  497. double speed = detect_speed (wav_data, !orig_pattern.empty());
  498. // speeds closer to 1.0 than this usually work without stretching before decode
  499. if (speed < 0.9999 || speed > 1.0001)
  500. {
  501. printf ("speed %.6f\n", speed);
  502. WavData wav_data_speed = resample (wav_data, Params::mark_sample_rate * speed);
  503. result_set.set_speed_pattern (true);
  504. BlockDecoder block_decoder;
  505. block_decoder.run (wav_data_speed, result_set);
  506. ClipDecoder clip_decoder;
  507. clip_decoder.run (wav_data_speed, result_set);
  508. result_set.set_speed_pattern (false);
  509. }
  510. }
  511. BlockDecoder block_decoder;
  512. block_decoder.run (wav_data, result_set);
  513. ClipDecoder clip_decoder;
  514. clip_decoder.run (wav_data, result_set);
  515. result_set.print();
  516. if (!orig_pattern.empty())
  517. {
  518. int match_count = result_set.print_match_count (orig_pattern);
  519. block_decoder.print_debug_sync();
  520. if (!match_count)
  521. return 1;
  522. }
  523. return 0;
  524. }
  525. int
  526. get_watermark (const string& infile, const string& orig_pattern)
  527. {
  528. WavData wav_data;
  529. Error err = wav_data.load (infile);
  530. if (err)
  531. {
  532. error ("audiowmark: error loading %s: %s\n", infile.c_str(), err.message());
  533. return 1;
  534. }
  535. if (Params::test_truncate)
  536. {
  537. const size_t want_n_samples = wav_data.sample_rate() * wav_data.n_channels() * Params::test_truncate;
  538. vector<float> short_samples = wav_data.samples();
  539. if (want_n_samples < short_samples.size())
  540. {
  541. short_samples.resize (want_n_samples);
  542. wav_data.set_samples (short_samples);
  543. }
  544. }
  545. if (wav_data.sample_rate() == Params::mark_sample_rate)
  546. {
  547. return decode_and_report (wav_data, orig_pattern);
  548. }
  549. else
  550. {
  551. return decode_and_report (resample (wav_data, Params::mark_sample_rate), orig_pattern);
  552. }
  553. }