├── .gitignore ├── Makefile ├── README.md ├── TODO ├── steg.c ├── steg.png ├── wavfile.c └── wavfile.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.wav 2 | steg 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc steg.c -o steg 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Example spectrogram of Steg's output](steg.png) 2 | 3 | ## What is Steg? 4 | 5 | It's a command line program written in C that converts plain text messages into .wav files, enabling you to hide messages within sound clips such as music. 6 | 7 | ## How do I use it? 8 | 9 | Clone the repo with `git clone git@github.com:ZoeB/steg.git`, go into that directory with `cd steg`, compile the program with `make`, and run it with a message you'd like to encode, such as `./steg 'Foo'`. Steg will create a new file, out.wav, with your message encoded in it. 10 | 11 | There are various command line arguments available. Run `./steg` without a message for a list of them. 12 | 13 | ## How can I read the message again? 14 | 15 | With a spectrogram of the waveform. There are many ways of generating a spectrogram. As an example, I'd recommend [Spek](http://spek.cc). 16 | 17 | ## Who made it? 18 | 19 | The bulk of the program was written by Zoë Blade. The .wav file writing routine was written by Douglas Thain. 20 | 21 | ## What license is it released under? 22 | 23 | It's released under the [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) license. 24 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Refactor code! 2 | -------------------------------------------------------------------------------- /steg.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Steg, by Zoe Blade 4 | 5 | Convert an ASCII message into a series of sine waves. 6 | 7 | This work is made available under the Creative Commons Attribution license. 8 | https://creativecommons.org/licenses/by/4.0/ 9 | 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "wavfile.c" 19 | 20 | #define M_TAU 2 * M_PI 21 | 22 | void usage(); 23 | 24 | FILE *wav; 25 | 26 | uint8_t charset[768] = { 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 28 | 0x00, 0x00, 0x18, 0x00, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 29 | 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00, 0x18, 0x3e, 0x60, 0x3c, 30 | 0x06, 0x7c, 0x18, 0x00, 0x62, 0x66, 0x0c, 0x18, 0x30, 0x66, 0x46, 0x00, 31 | 0x3c, 0x66, 0x3c, 0x38, 0x67, 0x66, 0x3f, 0x00, 0x06, 0x0c, 0x18, 0x00, 32 | 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 33 | 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x66, 0x3c, 0xff, 34 | 0x3c, 0x66, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x7e, 36 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 37 | 0x00, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x3c, 0x66, 0x6e, 0x76, 38 | 0x66, 0x66, 0x3c, 0x00, 0x18, 0x18, 0x38, 0x18, 0x18, 0x18, 0x7e, 0x00, 39 | 0x3c, 0x66, 0x06, 0x0c, 0x30, 0x60, 0x7e, 0x00, 0x3c, 0x66, 0x06, 0x1c, 40 | 0x06, 0x66, 0x3c, 0x00, 0x06, 0x0e, 0x1e, 0x66, 0x7f, 0x06, 0x06, 0x00, 41 | 0x7e, 0x60, 0x7c, 0x06, 0x06, 0x66, 0x3c, 0x00, 0x3c, 0x66, 0x60, 0x7c, 42 | 0x66, 0x66, 0x3c, 0x00, 0x7e, 0x66, 0x0c, 0x18, 0x18, 0x18, 0x18, 0x00, 43 | 0x3c, 0x66, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00, 0x3c, 0x66, 0x66, 0x3e, 44 | 0x06, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 45 | 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, 0x0e, 0x18, 0x30, 0x60, 46 | 0x30, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 47 | 0x70, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x70, 0x00, 0x3c, 0x66, 0x06, 0x0c, 48 | 0x18, 0x00, 0x18, 0x00, 0x3c, 0x66, 0x6e, 0x6e, 0x60, 0x62, 0x3c, 0x00, 49 | 0x18, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x7c, 0x66, 0x66, 0x7c, 50 | 0x66, 0x66, 0x7c, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3c, 0x00, 51 | 0x78, 0x6c, 0x66, 0x66, 0x66, 0x6c, 0x78, 0x00, 0x7e, 0x60, 0x60, 0x78, 52 | 0x60, 0x60, 0x7e, 0x00, 0x7e, 0x60, 0x60, 0x78, 0x60, 0x60, 0x60, 0x00, 53 | 0x3c, 0x66, 0x60, 0x6e, 0x66, 0x66, 0x3c, 0x00, 0x66, 0x66, 0x66, 0x7e, 54 | 0x66, 0x66, 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 55 | 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00, 0x66, 0x6c, 0x78, 0x70, 56 | 0x78, 0x6c, 0x66, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7e, 0x00, 57 | 0x63, 0x77, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x00, 0x66, 0x76, 0x7e, 0x7e, 58 | 0x6e, 0x66, 0x66, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 59 | 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x00, 0x3c, 0x66, 0x66, 0x66, 60 | 0x66, 0x3c, 0x0e, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x78, 0x6c, 0x66, 0x00, 61 | 0x3c, 0x66, 0x60, 0x3c, 0x06, 0x66, 0x3c, 0x00, 0x7e, 0x18, 0x18, 0x18, 62 | 0x18, 0x18, 0x18, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 63 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x63, 0x63, 0x63, 0x6b, 64 | 0x7f, 0x77, 0x63, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00, 65 | 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x7e, 0x06, 0x0c, 0x18, 66 | 0x30, 0x60, 0x7e, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 67 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 68 | 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x3e, 0x66, 0x3e, 0x00, 71 | 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x3c, 0x60, 72 | 0x60, 0x60, 0x3c, 0x00, 0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00, 73 | 0x00, 0x00, 0x3c, 0x66, 0x7e, 0x60, 0x3c, 0x00, 0x00, 0x0e, 0x18, 0x3e, 74 | 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x7c, 75 | 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x00, 0x00, 0x18, 0x00, 0x38, 76 | 0x18, 0x18, 0x3c, 0x00, 0x00, 0x06, 0x00, 0x06, 0x06, 0x06, 0x06, 0x3c, 77 | 0x00, 0x60, 0x60, 0x6c, 0x78, 0x6c, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 78 | 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x66, 0x7f, 0x7f, 0x6b, 0x63, 0x00, 79 | 0x00, 0x00, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3c, 0x66, 80 | 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x60, 81 | 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x7c, 0x66, 82 | 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x3e, 0x60, 0x3c, 0x06, 0x7c, 0x00, 83 | 0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x66, 0x66, 84 | 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 85 | 0x00, 0x00, 0x63, 0x6b, 0x7f, 0x3e, 0x36, 0x00, 0x00, 0x00, 0x66, 0x3c, 86 | 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3e, 0x0c, 0x78, 87 | 0x00, 0x00, 0x7e, 0x0c, 0x18, 0x30, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 91 | }; 92 | 93 | char c; 94 | int i = 0; 95 | int j; 96 | int messageLength; 97 | 98 | uint8_t byte; 99 | uint8_t byteLast; 100 | uint8_t byteNext; 101 | int charOffset; 102 | 103 | float sample; 104 | int freq; 105 | int freqNext; 106 | int col; 107 | int row; 108 | int copy; 109 | float pixel; 110 | float mix; 111 | short out; 112 | 113 | char var; 114 | int varc = 0; 115 | int value; 116 | 117 | int duplicates = 4; 118 | int fundamental = 16000; 119 | int harmonicSpacing = 500; 120 | int harmonicSpacingCurrent = 500; 121 | char output[72]; 122 | float samplerate = 44100; /* Ideally, this should be an int, but if I change it to an int, I should check if I need to e.g. multiply it by 1.0 in order to get the formula using it to output a float. */ 123 | int width = 11025; 124 | 125 | int main(int argc, char *argv[]) { 126 | FILE *filePointer; 127 | 128 | strcpy(output, "out.wav"); 129 | 130 | if (argc == 1) { 131 | /* Require a message to encode */ 132 | usage(); 133 | } 134 | 135 | while ((var = getopt(argc, argv, "d:f:h:o:s:w:")) != -1) { 136 | varc++; 137 | 138 | switch (var) { 139 | case 'd': 140 | value = atoi(optarg); 141 | 142 | if (value < 1 || value > 4) { 143 | fprintf(stderr, "d should be an integer between 1 and 4.\n"); 144 | exit(1); 145 | } 146 | 147 | duplicates = value; 148 | break; 149 | 150 | case 'f': 151 | value = atoi(optarg); 152 | 153 | if (value < 1 || value > 20000) { 154 | fprintf(stderr, "f should be an integer between 1 and 20000.\n"); 155 | exit(1); 156 | } 157 | 158 | fundamental = value; 159 | break; 160 | 161 | case 'h': 162 | if (strcmp(optarg, "exp") == 0) { 163 | harmonicSpacing = -2; 164 | break; 165 | } else if (strcmp(optarg, "lin") == 0) { 166 | harmonicSpacing = -1; 167 | break; 168 | } 169 | 170 | value = atoi(optarg); 171 | 172 | if (value < 1 || value > 2500) { 173 | fprintf(stderr, "h should be an integer between 1 and 2500, or lin, or exp.\n"); 174 | exit(1); 175 | } 176 | 177 | harmonicSpacing = value; 178 | break; 179 | 180 | case 'o': 181 | strcpy(output, optarg); 182 | break; 183 | 184 | case 's': 185 | value = atoi(optarg); 186 | 187 | if (value < 8000 || value > 48000) { 188 | fprintf(stderr, "s should be an integer between 8000 and 48000.\n"); 189 | exit(1); 190 | } 191 | 192 | samplerate = value; 193 | break; 194 | 195 | case 'w': 196 | value = atoi(optarg); 197 | 198 | if (value < 1 || value > 64000) { 199 | fprintf(stderr, "w should be an integer between 1 and 64000.\n"); 200 | exit(1); 201 | } 202 | 203 | width = value; 204 | break; 205 | 206 | default: 207 | usage(); 208 | } 209 | } 210 | 211 | wav = wavfile_open(output); 212 | 213 | if (!wav) { 214 | fprintf(stderr, "Error: unable to write to %s.\n", output); 215 | exit(1); 216 | } 217 | 218 | if (argc != (varc * 2) + 2) { 219 | fprintf(stderr, "Please specify exactly one message to encode.\n"); 220 | exit(1); 221 | } 222 | 223 | argv += argc - 1; 224 | messageLength = strlen(*argv); 225 | 226 | for (j = 0; j < messageLength; j++) { 227 | c = argv[0][j]; 228 | 229 | /* Only display printable ASCII characters */ 230 | if (c < 32 || c >= 126) { 231 | continue; 232 | } 233 | 234 | /* The character set file should only contain printable ASCII characters */ 235 | charOffset = 8 * (c - 32); 236 | byte = 0; 237 | 238 | for (col = 0; col < 8; col++) { 239 | byteLast = byte; 240 | byte = 0; 241 | 242 | for (row = 0; row < 8; row++) { 243 | if (charset[charOffset + row] & (1 << (7 - col))) { 244 | byte |= 1 << row; 245 | } 246 | } 247 | 248 | byteNext = 0; 249 | 250 | if (col != 7) { 251 | for (row = 0; row < 8; row++) { 252 | if (charset[charOffset + row] & (1 << (6 - col))) { 253 | byteNext |= 1 << row; 254 | } 255 | } 256 | } 257 | 258 | /* Loop through each sample */ 259 | for (sample = 0; sample < width; sample++) { /* Hardwire each pixel width as 1/4 of a CD quality second for now */ 260 | mix = 0; 261 | 262 | /* Work out which oscillators are on for this column */ 263 | for (row = 0; row < 8; row++) { 264 | if (byte & (1 << (7 - row))) { /* Lowest frequency oscillator first */ 265 | if (harmonicSpacing == -2) { 266 | // Exponential harmonic spacing 267 | freq = fundamental * pow(2, row); 268 | freqNext = fundamental * pow(2, row + 1); 269 | } else if (harmonicSpacing == -1) { 270 | // Linear harmonic spacing, as a multiple of the fundamental harmonic 271 | freq = fundamental * (row + 1); 272 | freqNext = fundamental * (row + 2); 273 | } else { 274 | // Linear harmonic spacing, as a specified amount 275 | freq = fundamental + (harmonicSpacing * row); 276 | freqNext = fundamental + (harmonicSpacing * (row + 1)); 277 | } 278 | 279 | // As per the Nyquist theorem, don't reflect back unobtainably high harmonics 280 | if (freq > samplerate / 2) { 281 | freq = samplerate / 2; 282 | } 283 | 284 | if (freqNext > samplerate / 2) { 285 | freqNext = samplerate / 2; 286 | } 287 | 288 | harmonicSpacingCurrent = freqNext - freq; 289 | pixel = 0; 290 | 291 | for (copy = 0; copy < duplicates; copy++) { 292 | pixel += sin((freq + (harmonicSpacingCurrent / duplicates * copy)) * (sample / samplerate) * M_TAU); /* The number of cycles per second is multiplied by the number of seconds. Even though the latter's between 0 and 0.25, the frequencies bring it up. Hardwire CD quality sample rate for now. */ 293 | } 294 | 295 | pixel /= duplicates; 296 | 297 | /* Fuzz off the edges of each off-adjacent pixel to avoid noise bursts */ 298 | if (sample < samplerate / 16 && !(byteLast & (1 << (7 - row)))) { 299 | pixel = pixel / (samplerate / 16) * sample; 300 | } else if (sample > (samplerate / 16 * 3) && !(byteNext & (1 << (7 - row)))) { 301 | pixel = pixel / (samplerate / 16) * (width - sample); 302 | } 303 | 304 | /* Fuzz off the top and bottom rows too */ 305 | if (copy == 0 || copy == duplicates - 1) { 306 | pixel /= 2; 307 | } 308 | 309 | mix += (1.0 / 9.0) * pixel; /* Each sine wave should be 1/9th volume, for mixing with headroom */ 310 | } 311 | } 312 | 313 | out = floor(mix * 32767); /* Convert waveform into signed 16-bit integer */ 314 | wavfile_write(wav, &out, 1); 315 | } 316 | } 317 | } 318 | 319 | wavfile_close(wav); 320 | return 0; 321 | } 322 | 323 | void usage() { 324 | fprintf(stderr, "Steg, by Zoe Blade\n\n"); 325 | fprintf(stderr, "usage: steg [-dfhosw] message\n\n"); 326 | fprintf(stderr, "OPTIONS:\n"); 327 | fprintf(stderr, " -d n duplicates of each line, including original, default 4\n"); 328 | fprintf(stderr, " -f n fundamental harmonic in Hz, default 16000\n"); 329 | fprintf(stderr, " -h n harmonic spacing in Hz (height of each pixel in Hz), or lin,\n"); 330 | fprintf(stderr, " or exp, default 500\n"); 331 | fprintf(stderr, " -o filename file to write to, default out.wav\n"); 332 | fprintf(stderr, " -s n samplerate in Hz, default 44100\n"); 333 | fprintf(stderr, " -w n width of each pixel in samples, default 11025\n\n"); 334 | exit(1); 335 | } 336 | -------------------------------------------------------------------------------- /steg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZoeB/steg/725ee2c9526c695ca96e8f0e3c5f2371039f7772/steg.png -------------------------------------------------------------------------------- /wavfile.c: -------------------------------------------------------------------------------- 1 | /* 2 | A simple sound library for CSE 20211 by Douglas Thain (dthain@nd.edu). 3 | This work is made available under the Creative Commons Attribution license. 4 | https://creativecommons.org/licenses/by/4.0/ 5 | 6 | For course assignments, you should not change this file. 7 | For complete documentation, see: 8 | http://www.nd.edu/~dthain/courses/cse20211/fall2013/wavfile 9 | */ 10 | 11 | #include "wavfile.h" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | struct wavfile_header { 18 | char riff_tag[4]; 19 | int riff_length; 20 | char wave_tag[4]; 21 | char fmt_tag[4]; 22 | int fmt_length; 23 | short audio_format; 24 | short num_channels; 25 | int sample_rate; 26 | int byte_rate; 27 | short block_align; 28 | short bits_per_sample; 29 | char data_tag[4]; 30 | int data_length; 31 | }; 32 | 33 | FILE * wavfile_open( const char *filename ) 34 | { 35 | struct wavfile_header header; 36 | 37 | int samples_per_second = WAVFILE_SAMPLES_PER_SECOND; 38 | int bits_per_sample = 16; 39 | 40 | strncpy(header.riff_tag,"RIFF",4); 41 | strncpy(header.wave_tag,"WAVE",4); 42 | strncpy(header.fmt_tag,"fmt ",4); 43 | strncpy(header.data_tag,"data",4); 44 | 45 | header.riff_length = 0; 46 | header.fmt_length = 16; 47 | header.audio_format = 1; 48 | header.num_channels = 1; 49 | header.sample_rate = samples_per_second; 50 | header.byte_rate = samples_per_second*(bits_per_sample/8); 51 | header.block_align = bits_per_sample/8; 52 | header.bits_per_sample = bits_per_sample; 53 | header.data_length = 0; 54 | 55 | FILE * file = fopen(filename,"w+"); 56 | if(!file) return 0; 57 | 58 | fwrite(&header,sizeof(header),1,file); 59 | 60 | fflush(file); 61 | 62 | return file; 63 | 64 | } 65 | 66 | void wavfile_write( FILE *file, short data[], int length ) 67 | { 68 | fwrite(data,sizeof(short),length,file); 69 | } 70 | 71 | void wavfile_close( FILE *file ) 72 | { 73 | int file_length = ftell(file); 74 | 75 | int data_length = file_length - sizeof(struct wavfile_header); 76 | fseek(file,sizeof(struct wavfile_header) - sizeof(int),SEEK_SET); 77 | fwrite(&data_length,sizeof(data_length),1,file); 78 | 79 | int riff_length = file_length - 8; 80 | fseek(file,4,SEEK_SET); 81 | fwrite(&riff_length,sizeof(riff_length),1,file); 82 | 83 | fclose(file); 84 | } 85 | 86 | -------------------------------------------------------------------------------- /wavfile.h: -------------------------------------------------------------------------------- 1 | /* 2 | A simple sound library for CSE 20211 by Douglas Thain. 3 | This work is made available under the Creative Commons Attribution license. 4 | https://creativecommons.org/licenses/by/4.0/ 5 | 6 | For course assignments, you should not change this file. 7 | For complete documentation, see: 8 | http://www.nd.edu/~dthain/courses/cse20211/fall2013/wavfile 9 | */ 10 | 11 | #ifndef WAVFILE_H 12 | #define WAVFILE_H 13 | 14 | #include 15 | #include 16 | 17 | FILE * wavfile_open( const char *filename ); 18 | void wavfile_write( FILE *file, short data[], int length ); 19 | void wavfile_close( FILE * file ); 20 | 21 | #define WAVFILE_SAMPLES_PER_SECOND 44100 22 | 23 | #endif 24 | --------------------------------------------------------------------------------