Init
This commit is contained in:
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
2
.idea/audiohide.iml
generated
Normal file
2
.idea/audiohide.iml
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module classpath="CMake" type="CPP_MODULE" version="4" />
|
||||
7
.idea/misc.xml
generated
Normal file
7
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CMakePythonSetting">
|
||||
<option name="pythonIntegrationState" value="YES" />
|
||||
</component>
|
||||
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/audiohide.iml" filepath="$PROJECT_DIR$/.idea/audiohide.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
7
CMakeLists.txt
Normal file
7
CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 4.1)
|
||||
project(audiohide C)
|
||||
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
|
||||
add_executable(audiohide main.c)
|
||||
target_link_libraries(audiohide sndfile fftw3f m)
|
||||
479
main.c
Normal file
479
main.c
Normal file
@@ -0,0 +1,479 @@
|
||||
/* watermark.c
|
||||
*
|
||||
* DSSS-style audio watermark embedder/extractor prototype in C.
|
||||
* Dependencies: libsndfile, fftw3f, math
|
||||
*
|
||||
* Build:
|
||||
* gcc -O2 -o audio_dsss watermark.c -lsndfile -lfftw3f -lm
|
||||
*
|
||||
* Modes:
|
||||
* embed in.wav out.wav message.txt seed
|
||||
* extract in.wav out.bin seed
|
||||
* test in.wav message.txt seed (creates watermarked.wav + extracted.bin and compares)
|
||||
*
|
||||
* Notes:
|
||||
* - Mono conversion: stereo averaged to mono.
|
||||
* - Adds 4-byte little-endian length prefix to payload.
|
||||
* - Uses r2c/c2r FFT for correct real-frame handling and proper normalization.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sndfile.h>
|
||||
#include <fftw3.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define FRAME_SIZE 2048
|
||||
#define HOP 512
|
||||
#define MIN_FREQ 1000.0
|
||||
#define MAX_FREQ 5000.0
|
||||
#define ALPHA 1.0f
|
||||
#define FRAMES_PER_BIT 4
|
||||
#define MAX_BITS 32768
|
||||
|
||||
static float hann_window(int n, int i) {
|
||||
return 0.5f * (1.0f - cosf(2.0f * M_PI * i / (n - 1)));
|
||||
}
|
||||
|
||||
static unsigned char *load_file(const char *path, size_t *out_size) {
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) { perror("fopen message"); return NULL; }
|
||||
fseek(f, 0, SEEK_END);
|
||||
long sz = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (sz <= 0) { fclose(f); *out_size = 0; return NULL; }
|
||||
unsigned char *buf = malloc(sz);
|
||||
if (!buf) { fclose(f); return NULL; }
|
||||
if (fread(buf, 1, sz, f) != (size_t)sz) { perror("fread"); free(buf); fclose(f); return NULL; }
|
||||
fclose(f);
|
||||
*out_size = sz;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int write_file(const char *path, const unsigned char *buf, size_t sz) {
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) { perror("fopen out"); return -1; }
|
||||
if (fwrite(buf, 1, sz, f) != sz) { perror("fwrite"); fclose(f); return -1; }
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* bytes -> bits LSB-first */
|
||||
static int bytes_to_bits(const unsigned char *bytes, size_t nbytes, int *bits, size_t *nbits) {
|
||||
size_t maxbits = nbytes * 8;
|
||||
if (maxbits > MAX_BITS) return -1;
|
||||
for (size_t i = 0; i < nbytes; ++i) {
|
||||
for (int b = 0; b < 8; ++b) bits[i*8 + b] = (bytes[i] >> b) & 1;
|
||||
}
|
||||
*nbits = maxbits;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* bits -> bytes LSB-first */
|
||||
static size_t bits_to_bytes(const int *bits, size_t nbits, unsigned char *out) {
|
||||
size_t nbytes = (nbits + 7) / 8;
|
||||
memset(out, 0, nbytes);
|
||||
for (size_t i = 0; i < nbits; ++i) if (bits[i]) out[i/8] |= (1u << (i%8));
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
/* xorshift32 */
|
||||
static uint32_t prng_next(uint32_t *state) {
|
||||
uint32_t x = *state;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
*state = x;
|
||||
return x;
|
||||
}
|
||||
|
||||
/* PN chips ±1 */
|
||||
static void generate_pn(int *chips, size_t len, uint32_t *seed) {
|
||||
for (size_t i = 0; i < len; ++i) chips[i] = (prng_next(seed) & 1) ? 1 : -1;
|
||||
}
|
||||
|
||||
/* frames count */
|
||||
static int frames_count(int signal_len, int frame_size, int hop) {
|
||||
if (signal_len < frame_size) return 0;
|
||||
return 1 + (signal_len - frame_size) / hop;
|
||||
}
|
||||
|
||||
/* mono conversion */
|
||||
static float *to_mono(const float *in, sf_count_t frames, int channels) {
|
||||
float *mono = malloc(sizeof(float) * frames);
|
||||
if (!mono) return NULL;
|
||||
if (channels == 1) {
|
||||
for (sf_count_t i = 0; i < frames; ++i) mono[i] = in[i];
|
||||
} else {
|
||||
for (sf_count_t i = 0; i < frames; ++i) {
|
||||
float s = 0.0f;
|
||||
for (int c = 0; c < channels; ++c) s += in[i*channels + c];
|
||||
mono[i] = s / channels;
|
||||
}
|
||||
}
|
||||
return mono;
|
||||
}
|
||||
|
||||
/* prepend 32-bit little-endian length to message buffer */
|
||||
static unsigned char *prepend_length(const unsigned char *msg, size_t msgsz, size_t *out_sz) {
|
||||
if (!msg || !out_sz) return NULL; // sanity check
|
||||
size_t total = msgsz + 4; // 4 bytes for length
|
||||
unsigned char *buf = malloc(total);
|
||||
if (!buf) return NULL;
|
||||
|
||||
// store message length in little-endian format
|
||||
uint32_t len32 = (uint32_t)msgsz;
|
||||
buf[0] = (unsigned char)(len32 & 0xFF);
|
||||
buf[1] = (unsigned char)((len32 >> 8) & 0xFF);
|
||||
buf[2] = (unsigned char)((len32 >> 16) & 0xFF);
|
||||
buf[3] = (unsigned char)((len32 >> 24) & 0xFF);
|
||||
|
||||
memcpy(buf + 4, msg, msgsz); // copy message after prefix
|
||||
*out_sz = total;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- Embed ---------- */
|
||||
static int embed(const char *in_wav, const char *out_wav, const char *message_path, uint32_t seed) {
|
||||
SF_INFO sfinfo;
|
||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||
SNDFILE *infile = sf_open(in_wav, SFM_READ, &sfinfo);
|
||||
if (!infile) { fprintf(stderr, "sf_open input failed: %s\n", in_wav); return 1; }
|
||||
|
||||
sf_count_t nframes = sfinfo.frames;
|
||||
int channels = sfinfo.channels;
|
||||
float *interleaved = malloc(sizeof(float) * nframes * channels);
|
||||
if (!interleaved) { sf_close(infile); return 1; }
|
||||
if (sf_readf_float(infile, interleaved, nframes) != nframes) {
|
||||
fprintf(stderr, "sf_readf_float failed\n"); free(interleaved); sf_close(infile); return 1;
|
||||
}
|
||||
sf_close(infile);
|
||||
|
||||
float *audio = to_mono(interleaved, nframes, channels);
|
||||
free(interleaved);
|
||||
if (!audio) { fprintf(stderr, "to_mono failed\n"); return 1; }
|
||||
|
||||
size_t msgsz;
|
||||
unsigned char *msg = load_file(message_path, &msgsz);
|
||||
if (!msg) { free(audio); fprintf(stderr, "load_file failed\n"); return 1; }
|
||||
|
||||
size_t msg_with_len_sz;
|
||||
unsigned char *msg_with_len = prepend_length(msg, msgsz, &msg_with_len_sz);
|
||||
free(msg);
|
||||
if (!msg_with_len) { free(audio); fprintf(stderr, "prepend_length failed\n"); return 1; }
|
||||
|
||||
int *bits = malloc(MAX_BITS * sizeof(int));
|
||||
size_t nbits;
|
||||
if (bytes_to_bits(msg_with_len, msg_with_len_sz, bits, &nbits) != 0) {
|
||||
fprintf(stderr, "message too large\n"); free(msg_with_len); free(audio); free(bits); return 1;
|
||||
}
|
||||
free(msg_with_len);
|
||||
|
||||
int frame_size = FRAME_SIZE;
|
||||
int hop = HOP;
|
||||
int nfft = frame_size;
|
||||
int nframes_stft = frames_count((int)nframes, frame_size, hop);
|
||||
if (nframes_stft <= 0) {
|
||||
fprintf(stderr, "audio too short for frame_size\n"); free(bits); free(audio); return 1;
|
||||
}
|
||||
|
||||
float *window = malloc(sizeof(float) * frame_size);
|
||||
for (int i = 0; i < frame_size; ++i) window[i] = hann_window(frame_size, i);
|
||||
|
||||
/* r2c: output bins = nfft/2 + 1 */
|
||||
int nbins = nfft/2 + 1;
|
||||
|
||||
fftwf_plan plan_fwd = fftwf_plan_dft_r2c_1d(nfft, NULL, NULL, FFTW_ESTIMATE);
|
||||
fftwf_plan plan_inv = fftwf_plan_dft_c2r_1d(nfft, NULL, NULL, FFTW_ESTIMATE);
|
||||
/* We'll create per-frame buffers */
|
||||
float *frame_in = fftwf_alloc_real(nfft);
|
||||
fftwf_complex *spec = fftwf_alloc_complex(nbins);
|
||||
float *ifft_out = fftwf_alloc_real(nfft);
|
||||
|
||||
/* Frequency bin indices for chosen band */
|
||||
double sr = sfinfo.samplerate;
|
||||
int bin_min = (int)floor(MIN_FREQ / sr * nfft);
|
||||
int bin_max = (int)ceil(MAX_FREQ / sr * nfft);
|
||||
if (bin_min < 1) bin_min = 1;
|
||||
if (bin_max > nfft/2) bin_max = nfft/2;
|
||||
int target_bins = bin_max - bin_min + 1;
|
||||
if (target_bins <= 0) { fprintf(stderr, "bad freq band selection\n"); goto cleanup_embed; }
|
||||
|
||||
float *out_audio = calloc(nframes, sizeof(float));
|
||||
if (!out_audio) { fprintf(stderr, "alloc out_audio failed\n"); goto cleanup_embed; }
|
||||
|
||||
int max_bits_possible = nframes_stft / FRAMES_PER_BIT;
|
||||
if ((int)nbits > max_bits_possible) {
|
||||
fprintf(stderr, "Warning: message (%zu bits) longer than capacity (%d bits). Truncating.\n", nbits, max_bits_possible);
|
||||
nbits = max_bits_possible;
|
||||
}
|
||||
|
||||
uint32_t prng = seed;
|
||||
int sample_idx = 0;
|
||||
int frame_no = 0;
|
||||
|
||||
/* create actual forward and inverse plans bound to buffers (since we need to execute them) */
|
||||
fftwf_destroy_plan(plan_fwd);
|
||||
fftwf_destroy_plan(plan_inv);
|
||||
plan_fwd = fftwf_plan_dft_r2c_1d(nfft, frame_in, spec, FFTW_MEASURE);
|
||||
plan_inv = fftwf_plan_dft_c2r_1d(nfft, spec, ifft_out, FFTW_MEASURE);
|
||||
|
||||
while (sample_idx + frame_size <= (int)nframes) {
|
||||
/* prepare input */
|
||||
for (int i = 0; i < nfft; ++i) {
|
||||
float v = 0.0f;
|
||||
if (i < frame_size) v = audio[sample_idx + i] * window[i];
|
||||
frame_in[i] = v;
|
||||
}
|
||||
fftwf_execute(plan_fwd);
|
||||
|
||||
int which_bit = frame_no / FRAMES_PER_BIT;
|
||||
if (which_bit < (int)nbits) {
|
||||
int b = bits[which_bit];
|
||||
int *chips = malloc(sizeof(int) * target_bins);
|
||||
generate_pn(chips, target_bins, &prng);
|
||||
for (int k = 0; k < target_bins; ++k) {
|
||||
int bin = bin_min + k;
|
||||
/* spec[bin] is complex: [real, imag] */
|
||||
float re = spec[bin][0];
|
||||
float im = spec[bin][1];
|
||||
float mag = sqrtf(re*re + im*im);
|
||||
float scale = ALPHA * (1.0f + mag);
|
||||
int sign = chips[k] * (b ? 1 : -1);
|
||||
/* small complex perturbation */
|
||||
spec[bin][0] += sign * scale * 0.5f;
|
||||
spec[bin][1] += sign * scale * 0.5f;
|
||||
}
|
||||
free(chips);
|
||||
}
|
||||
|
||||
/* inverse */
|
||||
fftwf_execute(plan_inv);
|
||||
|
||||
/* normalize and overlap-add */
|
||||
for (int i = 0; i < frame_size; ++i) {
|
||||
float v = ifft_out[i] / (float)nfft; /* normalization */
|
||||
out_audio[sample_idx + i] += v * window[i];
|
||||
}
|
||||
|
||||
sample_idx += hop;
|
||||
frame_no++;
|
||||
}
|
||||
|
||||
/* Fill any uncovered samples from original */
|
||||
for (int i = 0; i < (int)nframes; ++i) {
|
||||
if (!isfinite(out_audio[i])) out_audio[i] = audio[i];
|
||||
}
|
||||
|
||||
/* Write WAV */
|
||||
SF_INFO outinfo;
|
||||
memset(&outinfo, 0, sizeof(outinfo));
|
||||
outinfo.samplerate = sfinfo.samplerate;
|
||||
outinfo.channels = 1;
|
||||
outinfo.format = sfinfo.format;
|
||||
SNDFILE *outfile = sf_open(out_wav, SFM_WRITE, &outinfo);
|
||||
if (!outfile) { fprintf(stderr, "sf_open out failed\n"); free(out_audio); goto cleanup_embed; }
|
||||
if (sf_writef_float(outfile, out_audio, nframes) != nframes) {
|
||||
fprintf(stderr, "sf_writef_float failed\n");
|
||||
}
|
||||
sf_close(outfile);
|
||||
|
||||
printf("Embedding done. Embedded %zu bits (approx %zu bytes).\n", nbits, nbits/8);
|
||||
|
||||
free(out_audio);
|
||||
cleanup_embed:
|
||||
free(bits);
|
||||
fftwf_free(frame_in);
|
||||
fftwf_free(spec);
|
||||
fftwf_free(ifft_out);
|
||||
fftwf_destroy_plan(plan_fwd);
|
||||
fftwf_destroy_plan(plan_inv);
|
||||
free(window);
|
||||
free(audio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- Extract ---------- */
|
||||
static int extract(const char *in_wav, const char *out_msg_path, uint32_t seed) {
|
||||
SF_INFO sfinfo;
|
||||
memset(&sfinfo, 0, sizeof(sfinfo));
|
||||
SNDFILE *infile = sf_open(in_wav, SFM_READ, &sfinfo);
|
||||
if (!infile) { fprintf(stderr, "sf_open input failed: %s\n", in_wav); return 1; }
|
||||
|
||||
sf_count_t nframes = sfinfo.frames;
|
||||
int channels = sfinfo.channels;
|
||||
float *interleaved = malloc(sizeof(float) * nframes * channels);
|
||||
if (!interleaved) { sf_close(infile); return 1; }
|
||||
if (sf_readf_float(infile, interleaved, nframes) != nframes) {
|
||||
fprintf(stderr, "sf_readf_float failed\n"); free(interleaved); sf_close(infile); return 1;
|
||||
}
|
||||
sf_close(infile);
|
||||
|
||||
float *audio = to_mono(interleaved, nframes, channels);
|
||||
free(interleaved);
|
||||
if (!audio) { fprintf(stderr, "to_mono failed\n"); return 1; }
|
||||
|
||||
int frame_size = FRAME_SIZE;
|
||||
int hop = HOP;
|
||||
int nfft = frame_size;
|
||||
int nframes_stft = frames_count((int)nframes, frame_size, hop);
|
||||
if (nframes_stft <= 0) { fprintf(stderr, "audio too short for frame size\n"); free(audio); return 1; }
|
||||
|
||||
float *window = malloc(sizeof(float) * frame_size);
|
||||
for (int i = 0; i < frame_size; ++i) window[i] = hann_window(frame_size, i);
|
||||
|
||||
int nbins = nfft/2 + 1;
|
||||
fftwf_plan plan_fwd = fftwf_plan_dft_r2c_1d(nfft, NULL, NULL, FFTW_ESTIMATE);
|
||||
float *frame_in = fftwf_alloc_real(nfft);
|
||||
fftwf_complex *spec = fftwf_alloc_complex(nbins);
|
||||
|
||||
fftwf_destroy_plan(plan_fwd);
|
||||
plan_fwd = fftwf_plan_dft_r2c_1d(nfft, frame_in, spec, FFTW_MEASURE);
|
||||
|
||||
double sr = sfinfo.samplerate;
|
||||
int bin_min = (int)floor(MIN_FREQ / sr * nfft);
|
||||
int bin_max = (int)ceil(MAX_FREQ / sr * nfft);
|
||||
if (bin_min < 1) bin_min = 1;
|
||||
if (bin_max > nfft/2) bin_max = nfft/2;
|
||||
int target_bins = bin_max - bin_min + 1;
|
||||
if (target_bins <= 0) { fprintf(stderr, "bad freq band\n"); goto cleanup_extract; }
|
||||
|
||||
float *frame_corr = calloc(nframes_stft, sizeof(float));
|
||||
if (!frame_corr) { fprintf(stderr, "alloc frame_corr failed\n"); goto cleanup_extract; }
|
||||
|
||||
uint32_t prng = seed;
|
||||
int sample_idx = 0;
|
||||
int frame_no = 0;
|
||||
while (sample_idx + frame_size <= (int)nframes) {
|
||||
for (int i = 0; i < nfft; ++i) frame_in[i] = (i < frame_size) ? (audio[sample_idx + i] * window[i]) : 0.0f;
|
||||
fftwf_execute(plan_fwd);
|
||||
|
||||
int *chips = malloc(sizeof(int) * target_bins);
|
||||
generate_pn(chips, target_bins, &prng);
|
||||
|
||||
double corr = 0.0;
|
||||
for (int k = 0; k < target_bins; ++k) {
|
||||
int bin = bin_min + k;
|
||||
float re = spec[bin][0];
|
||||
float im = spec[bin][1];
|
||||
float mag = sqrtf(re*re + im*im) + 1e-9f;
|
||||
/* correlate normalized real part with chip */
|
||||
corr += (re / mag) * (double)chips[k];
|
||||
}
|
||||
frame_corr[frame_no] = (float)corr;
|
||||
free(chips);
|
||||
|
||||
sample_idx += hop;
|
||||
frame_no++;
|
||||
}
|
||||
|
||||
int max_possible_bits = nframes_stft / FRAMES_PER_BIT;
|
||||
int *recovered_bits = malloc(sizeof(int) * max_possible_bits);
|
||||
if (!recovered_bits) { fprintf(stderr, "alloc recovered_bits failed\n"); goto cleanup_extract; }
|
||||
|
||||
for (int b = 0; b < max_possible_bits; ++b) {
|
||||
double sum = 0.0;
|
||||
for (int f = 0; f < FRAMES_PER_BIT; ++f) {
|
||||
int fi = b * FRAMES_PER_BIT + f;
|
||||
if (fi >= nframes_stft) break;
|
||||
sum += frame_corr[fi];
|
||||
}
|
||||
recovered_bits[b] = (sum > 0.0) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Convert to bytes */
|
||||
size_t nbits_out = max_possible_bits;
|
||||
unsigned char *outbytes = malloc((nbits_out/8 + 8));
|
||||
memset(outbytes, 0, (nbits_out/8 + 8));
|
||||
size_t nbytes = bits_to_bytes(recovered_bits, nbits_out, outbytes);
|
||||
|
||||
/* Read first 4 bytes as length prefix (little-endian) */
|
||||
if (nbytes < 4) {
|
||||
fprintf(stderr, "extracted data too small to contain length prefix\n");
|
||||
write_file(out_msg_path, outbytes, nbytes);
|
||||
goto cleanup_extract;
|
||||
}
|
||||
uint32_t claimed_len = (uint32_t)outbytes[0] | ((uint32_t)outbytes[1] << 8) | ((uint32_t)outbytes[2] << 16) | ((uint32_t)outbytes[3] << 24);
|
||||
if (claimed_len > nbytes - 4) {
|
||||
fprintf(stderr, "warning: claimed length %u larger than extracted bytes %zu (truncated)\n", claimed_len, nbytes - 4);
|
||||
claimed_len = (uint32_t)(nbytes - 4);
|
||||
}
|
||||
|
||||
if (write_file(out_msg_path, outbytes + 4, claimed_len) != 0) {
|
||||
fprintf(stderr, "write_file failed\n");
|
||||
} else {
|
||||
printf("Extraction produced %u bytes (after length prefix).\n", claimed_len);
|
||||
}
|
||||
|
||||
cleanup_extract:
|
||||
if (frame_corr) free(frame_corr);
|
||||
if (recovered_bits) free(recovered_bits);
|
||||
if (outbytes) free(outbytes);
|
||||
fftwf_free(frame_in);
|
||||
fftwf_free(spec);
|
||||
fftwf_destroy_plan(plan_fwd);
|
||||
free(window);
|
||||
free(audio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- main ---------- */
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage:\n %s embed in.wav out.wav message.txt seed\n %s extract in.wav out.bin seed\n %s test in.wav message.txt seed\n", argv[0], argv[0], argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(argv[1], "embed") == 0) {
|
||||
if (argc < 6) { fprintf(stderr, "embed needs in out message seed\n"); return 1; }
|
||||
uint32_t seed = (uint32_t)atoi(argv[5]);
|
||||
return embed(argv[2], argv[3], argv[4], seed);
|
||||
} else if (strcmp(argv[1], "extract") == 0) {
|
||||
if (argc < 5) { fprintf(stderr, "extract needs in out seed\n"); return 1; }
|
||||
uint32_t seed = (uint32_t)atoi(argv[4]);
|
||||
return extract(argv[2], argv[3], seed);
|
||||
} else if (strcmp(argv[1], "test") == 0) {
|
||||
if (argc < 5) { fprintf(stderr, "test needs in message seed\n"); return 1; }
|
||||
const char *inwav = argv[2];
|
||||
const char *msg = argv[3];
|
||||
uint32_t seed = (uint32_t)atoi(argv[4]);
|
||||
const char *watermarked = "watermarked.wav";
|
||||
const char *extracted = "extracted.bin";
|
||||
printf("Test mode: embedding %s into %s with seed %u -> %s\n", msg, inwav, seed, watermarked);
|
||||
int r = embed(inwav, watermarked, msg, seed);
|
||||
if (r != 0) { fprintf(stderr, "embed failed\n"); return r; }
|
||||
printf("Now extracting from %s -> %s\n", watermarked, extracted);
|
||||
r = extract(watermarked, extracted, seed);
|
||||
if (r != 0) { fprintf(stderr, "extract failed\n"); return r; }
|
||||
/* compare original message and extracted */
|
||||
size_t orig_sz;
|
||||
unsigned char *orig = load_file(msg, &orig_sz);
|
||||
size_t ext_sz;
|
||||
unsigned char *ext = load_file(extracted, &ext_sz);
|
||||
if (!orig || !ext) {
|
||||
fprintf(stderr, "couldn't read original or extracted for comparison\n");
|
||||
free(orig); free(ext);
|
||||
return 0;
|
||||
}
|
||||
if (orig_sz == ext_sz && memcmp(orig, ext, orig_sz) == 0) {
|
||||
printf("SUCCESS: extracted matches original (%zu bytes)\n", orig_sz);
|
||||
} else {
|
||||
printf("MISMATCH: original %zu bytes vs extracted %zu bytes\n", orig_sz, ext_sz);
|
||||
/* helpful hex diff of first 32 bytes */
|
||||
size_t show = (orig_sz < ext_sz ? orig_sz : ext_sz);
|
||||
if (show > 32) show = 32;
|
||||
printf("Original first %zu bytes: ", show);
|
||||
for (size_t i = 0; i < show; ++i) printf("%02X ", orig[i]);
|
||||
printf("\nExtracted first %zu bytes: ", show);
|
||||
for (size_t i = 0; i < show; ++i) printf("%02X ", ext[i]);
|
||||
printf("\n");
|
||||
}
|
||||
free(orig); free(ext);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "unknown mode: %s\n", argv[1]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user