├── Makefile ├── README.md ├── LICENSE └── main.cpp /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | FLAGS=-Wall -O2 3 | 4 | demodulate-ook: main.cpp 5 | $(CC) $(FLAGS) -o demodulate-ook main.cpp 6 | 7 | clean: 8 | -rm demodulate-ook 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DemodulateOOK 2 | Demodulate on/off keying. 3 | 4 | ## Usage 5 | ``` 6 | ./demodulate-ook file-name 7 | ``` 8 | If the file isn't detected as a wav file it will assume it is 16 bits/sample, 1 channel, signed integers, and little endian. 9 | 10 | ## "Issues" 11 | * When using 32 bit samples it needs to allocate 16 GiB (4*2^32 bytes) of RAM. 12 | * Messes up if there are >256 bits set to on or off. Ignoring the beginning and the end of the data and anything longer than 96000 samples that don't switch state. 13 | * 10 samples/bit is the minimum. 14 | * You can't select which channel to use. It just uses the first channel. 15 | * There are issues with clock skew and amplitude skew. For short messages this shouldn't be a problem. 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Steve Thomas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Steve "Sc00bz" Thomas (steve at tobtu dot com) 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | // Max span of on or off is 2 seconds at 48 kHz 27 | #define MAX_SPAN (2*48000) 28 | // Samples needed to change on/off 29 | #define RADIO_FLICKER 5 30 | 31 | struct wavHeader 32 | { 33 | uint32_t tag; // "RIFF" 34 | uint32_t fileSize; // realFileSize-8 35 | uint32_t type; // "WAVE" 36 | uint32_t chunkMarker; // "fmt " 37 | uint32_t fileSizeSoFar; // 16 38 | uint16_t format; // 1 = PCM 39 | uint16_t channels; 40 | uint32_t sampleRate; 41 | uint32_t byteRate; 42 | uint16_t bytesPerSample; 43 | uint16_t bitsPerSample; 44 | uint32_t dataTag; // "data" 45 | uint32_t dataSize; 46 | }; 47 | 48 | /** 49 | * Make a file format value give the file's format. 50 | * 51 | * @param sampleByteSize - The sample size in bytes (1 to 4) 52 | * @param channels - Number of channels (1 to 256) 53 | * @param channel - The channel number (0 to 255) 54 | * @param isSigned - If samples are signed integer (0 or 1) 55 | * @param isLittleEndian - If samples are in little endian (0 or 1) 56 | * @return The file format value 57 | */ 58 | uint32_t makeFileFormat(uint32_t sampleByteSize, uint32_t channels, uint32_t channel, uint32_t isSigned, uint32_t isLittleEndian) 59 | { 60 | return 61 | ((sampleByteSize - 1) & 3) | 62 | (((channels - 1) & 0xff) << 2) | 63 | (( channel & 0xff) << 10) | 64 | (( isSigned & 1) << 18) | 65 | (( isLittleEndian & 1) << 19); 66 | } 67 | 68 | /** 69 | * Make a file format value give the file's format. 70 | * 71 | * @param fileFormat - The file format 72 | * @return The sample size in bytes (1 to 4) 73 | */ 74 | uint32_t getSampleByteSize(uint32_t fileFormat) 75 | { 76 | return (fileFormat & 3) + 1; 77 | } 78 | 79 | /** 80 | * Reads a sample from the input file. 81 | * 82 | * @param fin - The input file at an offset into the data 83 | * @param fileFormat - The file format 84 | * @param error - Set to non-zero if there's an error 85 | * @return The value of the sample or UINT32_MAX on error 86 | */ 87 | uint32_t getSample(FILE *fin, uint32_t fileFormat, uint32_t *error = NULL) 88 | { 89 | uint32_t sampleSize = ( fileFormat & 3) + 1; 90 | uint32_t channels = ((fileFormat >> 2) & 0xff) + 1; 91 | uint32_t channel = (fileFormat >> 10) & 0xff; 92 | uint32_t isSigned = (fileFormat >> 18) & 1; 93 | uint32_t isLittleEndian = (fileFormat >> 19) & 1; 94 | uint32_t sample = 0; 95 | uint8_t sample8[4]; 96 | 97 | if (channels > 1) 98 | { 99 | if (fseek(fin, sampleSize * channel, SEEK_CUR)) 100 | { 101 | if (!feof(fin)) 102 | { 103 | perror("fseek"); 104 | } 105 | if (error != NULL) 106 | { 107 | *error = 1; 108 | } 109 | return UINT32_MAX; 110 | } 111 | } 112 | if (fread(sample8, sampleSize, 1, fin) != 1) 113 | { 114 | if (!feof(fin)) 115 | { 116 | perror("fread"); 117 | } 118 | if (error != NULL) 119 | { 120 | *error = 1; 121 | } 122 | return UINT32_MAX; 123 | } 124 | if (channels > 1) 125 | { 126 | if (fseek(fin, sampleSize * (channels - channel - 1), SEEK_CUR)) 127 | { 128 | if (!feof(fin)) 129 | { 130 | perror("fseek"); 131 | } 132 | if (error != NULL) 133 | { 134 | *error = 1; 135 | } 136 | return UINT32_MAX; 137 | } 138 | } 139 | if (error != NULL) 140 | { 141 | *error = 0; 142 | } 143 | 144 | // Endian 145 | if (isLittleEndian) 146 | { 147 | for (int i = (int) sampleSize - 1; i >= 0; i--) 148 | { 149 | sample <<= 8; 150 | sample |= sample8[i]; 151 | } 152 | } 153 | else 154 | { 155 | for (uint32_t i = 0; i < sampleSize; i++) 156 | { 157 | sample <<= 8; 158 | sample |= sample8[i]; 159 | } 160 | } 161 | 162 | // Signed to unsigned 163 | if (isSigned) 164 | { 165 | sample = (sample + (1 << (8 * sampleSize - 1))) & ((1 << (8 * sampleSize)) - 1); 166 | } 167 | 168 | return sample; 169 | } 170 | 171 | /** 172 | * Counts samples of each value. 173 | * 174 | * @param counts - A pointer to integers that receive the number of samples with said value 175 | * @param fin - The input file at the offset of where the data starts 176 | * @param fileFormat - The file format 177 | * @return The total number of samples or UINT32_MAX on error 178 | */ 179 | uint32_t getCounts(uint32_t *counts, FILE *fin, uint32_t fileFormat) 180 | { 181 | uint32_t count = 0; 182 | uint32_t error = 0; 183 | size_t numCounts = ((size_t) 1) << (8 * getSampleByteSize(fileFormat)); 184 | 185 | for (size_t i = 0; i < numCounts; i++) 186 | { 187 | counts[i] = 0; 188 | } 189 | while (!feof(fin)) 190 | { 191 | uint32_t sample = getSample(fin, fileFormat, &error); 192 | 193 | if (error) 194 | { 195 | if (feof(fin)) 196 | { 197 | break; 198 | } 199 | return UINT32_MAX; 200 | } 201 | 202 | counts[sample]++; 203 | count++; 204 | } 205 | return count; 206 | } 207 | 208 | /** 209 | * Finds a threshold value that anything above the value is on and anything below is off. 210 | * This is done by averaging the highest sample and lowest sample, ignoring the highest 2% and lowest 2% of samples. 211 | * 212 | * @param counts - A constant pointer to integers that were generated from calling getCounts() 213 | * @param count - The total number of samples 214 | * @param fileFormat - The file format 215 | * @return The threshold value between on and off 216 | */ 217 | uint32_t findOnOffThreshold(const uint32_t *counts, uint32_t count, uint32_t fileFormat) 218 | { 219 | size_t numCounts = ((size_t) 1) << (8 * getSampleByteSize(fileFormat)); 220 | uint32_t hi = 0; 221 | uint32_t lo = (uint32_t) (numCounts - 1); 222 | uint32_t skipCount = count / 50; // 2% 223 | uint32_t curCount = 0; 224 | 225 | // Get hi and lo 226 | for (size_t i = 0; i < numCounts; i++) 227 | { 228 | if (counts[i] != 0) 229 | { 230 | curCount += counts[i]; 231 | if (curCount > skipCount) 232 | { 233 | lo = (uint32_t) i; 234 | break; 235 | } 236 | } 237 | } 238 | curCount = 0; 239 | for (uint32_t i = (uint32_t) (numCounts - 1); i > lo; i--) 240 | { 241 | if (counts[i] != 0) 242 | { 243 | curCount += counts[i]; 244 | if (curCount > skipCount) 245 | { 246 | hi = i; 247 | break; 248 | } 249 | } 250 | } 251 | if (lo >= hi) 252 | { 253 | return 0; 254 | } 255 | return (hi + lo) / 2; 256 | } 257 | 258 | /** 259 | * Moves the file offset past the initial on/off state. 260 | * 261 | * @param state - Set to the first state (on/off) 262 | * @param radioFlicker - Number samples needed to change the state 263 | * @param onOffThreshold - The threshold value between on and off 264 | * @param fin - The input file at the offset of where the data starts 265 | * @param fileFormat - The file format 266 | * @return leftOver (used by subsequent calls to getNextSpan()) 267 | */ 268 | uint32_t ignoreFirstSpan(uint32_t &state, uint32_t radioFlicker, uint32_t onOffThreshold, FILE *fin, uint32_t fileFormat) 269 | { 270 | uint32_t nextCount = 0; 271 | uint32_t curState; 272 | uint32_t newState; 273 | uint32_t sample; 274 | uint32_t error = 0; 275 | 276 | // Get state 277 | sample = getSample(fin, fileFormat, &error); 278 | if (error) 279 | { 280 | if (feof(fin)) 281 | { 282 | return 0; 283 | } 284 | return UINT32_MAX; 285 | } 286 | 287 | curState = 1; // on 288 | if (sample < onOffThreshold) 289 | { 290 | curState = 0; // off 291 | } 292 | 293 | // Read samples 294 | while (!feof(fin)) 295 | { 296 | sample = getSample(fin, fileFormat, &error); 297 | if (error) 298 | { 299 | if (feof(fin)) 300 | { 301 | break; 302 | } 303 | return UINT32_MAX; 304 | } 305 | 306 | newState = 1; // on 307 | if (sample < onOffThreshold) 308 | { 309 | newState = 0; // off 310 | } 311 | if (newState != curState) 312 | { 313 | nextCount++; 314 | if (nextCount > radioFlicker) 315 | { 316 | return nextCount; 317 | } 318 | } 319 | else 320 | { 321 | nextCount = 0; 322 | } 323 | } 324 | return 0; 325 | } 326 | 327 | /** 328 | * Moves the file offset to the next on/off state and returns number of samples in the current state. 329 | * 330 | * @param state - Set to the first state (on/off). Do not modify this between call of ignoreFirstSpan() or getNextSpan() 331 | * @param radioFlicker - Number samples needed to change the state 332 | * @param onOffThreshold - The threshold value between on and off 333 | * @param fin - The input file at the offset of where the data starts 334 | * @param fileFormat - The file format 335 | * @param leftOver - The left over from the previous call of ignoreFirstSpan() or getNextSpan() 336 | * @return The number of samples of state 337 | */ 338 | uint32_t getNextSpan(uint32_t &state, uint32_t radioFlicker, uint32_t onOffThreshold, FILE *fin, uint32_t fileFormat, uint32_t &leftOver) 339 | { 340 | uint32_t count = leftOver; 341 | uint32_t nextCount = 0; 342 | uint32_t curState = 0; 343 | uint32_t newState; 344 | uint32_t error = 0; 345 | 346 | // Flip state 347 | if (state == 0) 348 | { 349 | curState = 1; 350 | } 351 | state = curState; 352 | 353 | // Read samples 354 | while (!feof(fin)) 355 | { 356 | uint32_t sample = getSample(fin, fileFormat, &error); 357 | 358 | if (error) 359 | { 360 | if (feof(fin)) 361 | { 362 | break; 363 | } 364 | return UINT32_MAX; 365 | } 366 | 367 | newState = 1; // on 368 | if (sample < onOffThreshold) 369 | { 370 | newState = 0; // off 371 | } 372 | if (newState != curState) 373 | { 374 | nextCount++; 375 | if (nextCount > radioFlicker) 376 | { 377 | leftOver = nextCount; 378 | return count; 379 | } 380 | } 381 | else 382 | { 383 | count += nextCount + 1; 384 | nextCount = 0; 385 | } 386 | } 387 | 388 | // Ignore last span 389 | return 0; 390 | } 391 | 392 | /** 393 | * Counts spans of samples in either on or off states. 394 | * 395 | * @param spans - An array of maxSpan+1 integers 396 | * @param maxSpan - The max span to record 397 | * @param onOffThreshold - The threshold value between on and off 398 | * @param fin - The input file at the offset of where the data starts 399 | * @param fileFormat - The file format 400 | * @return The max span or UINT32_MAX on error 401 | */ 402 | uint32_t getSpans(uint32_t *spans, uint32_t maxSpan, uint32_t onOffThreshold, FILE *fin, uint32_t fileFormat) 403 | { 404 | uint32_t leftOver; 405 | uint32_t realMaxSpan = 0; 406 | uint32_t count; 407 | uint32_t state = 0; // 0 = off, 1 = on 408 | 409 | if (maxSpan == UINT32_MAX) 410 | { 411 | maxSpan = UINT32_MAX - 1; 412 | } 413 | for (uint32_t i = 0; i <= maxSpan; i++) 414 | { 415 | spans[i] = 0; 416 | } 417 | leftOver = ignoreFirstSpan(state, RADIO_FLICKER, onOffThreshold, fin, fileFormat); 418 | if (leftOver == UINT32_MAX) 419 | { 420 | return UINT32_MAX; 421 | } 422 | 423 | while (1) 424 | { 425 | count = getNextSpan(state, RADIO_FLICKER, onOffThreshold, fin, fileFormat, leftOver); 426 | if (count == UINT32_MAX) 427 | { 428 | return UINT32_MAX; 429 | } 430 | if (count == 0) 431 | { 432 | break; // EOF 433 | } 434 | 435 | if (realMaxSpan < count) 436 | { 437 | realMaxSpan = count; 438 | } 439 | if (count <= maxSpan) 440 | { 441 | spans[count]++; 442 | } 443 | } 444 | return realMaxSpan; 445 | } 446 | 447 | /** 448 | * Finds the width of a single bit in number of samples. 449 | * 450 | * @param spans - An array of at least maxSpan+1 integers 451 | * @param maxSpan - The max span to record 452 | * @return The width of a single bit in number of samples or 0 on error 453 | */ 454 | uint32_t findSingleBitWidth(const uint32_t *spans, uint32_t maxSpan) 455 | { 456 | double minErr = 1e99; 457 | double curErr; 458 | uint32_t hi = 0; 459 | uint32_t lo = maxSpan; 460 | uint32_t bestSingleBitWidth = 0; 461 | 462 | // Get hi and lo 463 | for (uint32_t i = 0; i < maxSpan; i++) 464 | { 465 | if (spans[i] != 0) 466 | { 467 | lo = i; 468 | break; 469 | } 470 | } 471 | for (uint32_t i = maxSpan; i > lo; i--) 472 | { 473 | if (spans[i] != 0) 474 | { 475 | hi = i; 476 | break; 477 | } 478 | } 479 | if (lo > hi) 480 | { 481 | return 0; 482 | } 483 | 484 | // min 10 samples/bit 485 | // max 256 bits without change 486 | uint32_t endAt = hi / 256; 487 | if (endAt < 10) 488 | { 489 | endAt = 10; 490 | } 491 | 492 | // Brute force... meh 493 | for (uint32_t i = hi; i >= endAt; i--) 494 | { 495 | curErr = 0; 496 | for (uint32_t j = lo; j < hi; j++) 497 | { 498 | if (spans[j] != 0) 499 | { 500 | uint32_t error; 501 | double scaledError; 502 | 503 | error = j % i; 504 | if (error > i - error) 505 | { 506 | error = i - error; 507 | } 508 | scaledError = (double) error / i; 509 | curErr += scaledError * scaledError * spans[j]; 510 | } 511 | } 512 | if (minErr > curErr) 513 | { 514 | bestSingleBitWidth = i; 515 | minErr = curErr; 516 | } 517 | } 518 | return bestSingleBitWidth; 519 | } 520 | 521 | /** 522 | * Outputs the data. 523 | * 524 | * @param singleBitWidth - The width of a single bit in number of samples 525 | * @param onOffThreshold - The threshold value between on and off 526 | * @param fin - The input file at the offset of where the data starts 527 | * @param fileFormat - The file format 528 | * @return The bit length of the data or UINT32_MAX on error 529 | */ 530 | uint32_t printMessage(uint32_t singleBitWidth, uint32_t onOffThreshold, FILE *fin, uint32_t fileFormat) 531 | { 532 | uint32_t bitLength = 0; 533 | uint32_t leftOver; 534 | uint32_t samples; 535 | uint32_t bits; 536 | uint32_t state = 0; // 0 = off, 1 = on 537 | uint8_t currentByte = 0; 538 | 539 | leftOver = ignoreFirstSpan(state, RADIO_FLICKER, onOffThreshold, fin, fileFormat); 540 | if (leftOver == UINT32_MAX) 541 | { 542 | return UINT32_MAX; 543 | } 544 | 545 | while (1) 546 | { 547 | samples = getNextSpan(state, RADIO_FLICKER, onOffThreshold, fin, fileFormat, leftOver); 548 | if (samples == UINT32_MAX) 549 | { 550 | return UINT32_MAX; 551 | } 552 | if (samples == 0) 553 | { 554 | break; // EOF 555 | } 556 | 557 | // Round to the nearest number of bits 558 | bits = (samples + singleBitWidth / 2) / singleBitWidth; 559 | 560 | uint32_t left = 8 - bitLength % 8; 561 | bitLength += bits; 562 | if (state == 0) // off 563 | { 564 | if (left <= bits) 565 | { 566 | printf("%02x", currentByte); 567 | currentByte = 0; 568 | 569 | // full bytes 570 | uint32_t fullBytes = (bits - left) / 8; 571 | for (uint32_t i = 0; i < fullBytes; i++) 572 | { 573 | printf("00"); 574 | } 575 | } 576 | } 577 | else // on 578 | { 579 | if (left <= bits) 580 | { 581 | currentByte |= (1 << left) - 1; 582 | printf("%02x", currentByte); 583 | currentByte = 0; 584 | 585 | // full bytes 586 | uint32_t fullBytes = (bits - left) / 8; 587 | for (uint32_t i = 0; i < fullBytes; i++) 588 | { 589 | printf("ff"); 590 | } 591 | bits -= 8 * fullBytes + left; 592 | left = 8; 593 | } 594 | // extra 595 | currentByte |= (1 << left) - (1 << (left - bits)); 596 | } 597 | } 598 | if (bitLength % 8 != 0) 599 | { 600 | printf("%02x", currentByte); 601 | } 602 | printf("\n"); 603 | return bitLength; 604 | } 605 | 606 | int main(int argc, char *argv[]) 607 | { 608 | uint32_t *counts; 609 | uint32_t *spans = new uint32_t[MAX_SPAN + 1]; 610 | FILE *fin; 611 | wavHeader header; 612 | uint32_t startOffset = 0; 613 | uint32_t fileSize; 614 | uint32_t count; 615 | uint32_t bitLength; 616 | uint32_t singleBitWidth; 617 | // 16 bits/sample, 1 channel, signed integers, little endian 618 | uint32_t fileFormat = makeFileFormat(2, 1, 0, 1, 1); 619 | uint32_t onOffThreshold; 620 | 621 | if (argc != 2) 622 | { 623 | fprintf(stderr, "usage:\n\"%s\" file-name\n", argv[0]); 624 | return 1; 625 | } 626 | 627 | // Open wav 628 | fin = fopen(argv[1], "rb"); 629 | if (fin == NULL) 630 | { 631 | perror("fopen"); 632 | return 1; 633 | } 634 | 635 | // File size 636 | fseek(fin, 0, SEEK_END); 637 | fileSize = ftell(fin); 638 | fseek(fin, 0, SEEK_SET); 639 | 640 | // Read wav header 641 | if (fileSize >= 44) 642 | { 643 | if (fread(&header, sizeof(wavHeader), 1, fin) != 1) 644 | { 645 | perror("fread"); 646 | return 1; 647 | } 648 | 649 | if (header.tag == 0x46464952 && // "RIFF" 650 | header.fileSize == fileSize - 8 && 651 | header.type == 0x45564157 && // "WAVE" 652 | header.chunkMarker == 0x20746d66 && // "fmt " 653 | header.fileSizeSoFar == 16 && 654 | header.format == 1 && // PCM 655 | header.dataTag == 0x61746164 && // "data" 656 | header.dataSize == fileSize - 44) 657 | { 658 | if (header.channels == 0 || 659 | header.channels > 256 || 660 | header.bitsPerSample % 8 != 0 || 661 | header.bitsPerSample == 0 || 662 | header.bitsPerSample > 32) 663 | { 664 | fprintf(stderr, "Error: Only supports raw 16 bit signed data and 8, 16, 24, 32 bit .wav with <257 channels\n"); 665 | return 1; 666 | } 667 | else 668 | { 669 | printf("File is a .wav\n"); 670 | startOffset = 44; 671 | fileFormat = makeFileFormat(header.bitsPerSample / 8, header.channels, 0, 1, 1); 672 | } 673 | } 674 | else 675 | { 676 | printf("Assuming file is raw 16 bit signed data\n"); 677 | fseek(fin, 0, SEEK_SET); 678 | } 679 | } 680 | if (startOffset == 0) 681 | { 682 | if (fileSize % 2 == 1) 683 | { 684 | fprintf(stderr, "Error: Only supports raw 16 bit signed data and 8, 16, 24, 32 bit .wav with <257 channels\n"); 685 | return 1; 686 | } 687 | } 688 | 689 | size_t numCounts = ((size_t) 1) << (8 * getSampleByteSize(fileFormat)); 690 | // Check for size overflow 691 | if (numCounts == 0) 692 | { 693 | fprintf(stderr, "Error: 32 bit samples requires a 64 bit binary and 16 GiB of RAM.\n"); 694 | return 1; 695 | } 696 | counts = new uint32_t[numCounts]; 697 | 698 | // Count samples 699 | printf("Counting...\n"); 700 | count = getCounts(counts, fin, fileFormat); 701 | if (count == UINT32_MAX) 702 | { 703 | return 1; 704 | } 705 | fseek(fin, startOffset, SEEK_SET); 706 | 707 | // Finding on off ranges 708 | printf("Finding on off ranges...\n"); 709 | onOffThreshold = findOnOffThreshold(counts, count, fileFormat); 710 | if (onOffThreshold == 0) 711 | { 712 | fprintf(stderr, "Error: Can't find on off ranges\n"); 713 | return 1; 714 | } 715 | 716 | // Getting spans 717 | printf("Getting spans...\n"); 718 | uint32_t realMaxSpan = getSpans(spans, MAX_SPAN, onOffThreshold, fin, fileFormat); 719 | if (realMaxSpan == UINT32_MAX) 720 | { 721 | fprintf(stderr, "Error: 1\n"); 722 | return 1; 723 | } 724 | fseek(fin, startOffset, SEEK_SET); 725 | 726 | // Finding single bit width 727 | printf("Finding single bit width...\n"); 728 | singleBitWidth = findSingleBitWidth(spans, realMaxSpan); 729 | if (singleBitWidth == 0) 730 | { 731 | fprintf(stderr, "Error: 2\n"); 732 | return 1; 733 | } 734 | 735 | // Print bit width 736 | printf("samples/bit: %u\n", singleBitWidth); 737 | if (startOffset != 0) // .wav 738 | { 739 | printf("seconds/bit: %0.9f\n", (double) singleBitWidth / header.sampleRate); 740 | printf("bits/second: %0.3f\n", (double) header.sampleRate / singleBitWidth); 741 | } 742 | 743 | // Print message 744 | bitLength = printMessage(singleBitWidth, onOffThreshold, fin, fileFormat); 745 | if (bitLength == UINT32_MAX) 746 | { 747 | fprintf(stderr, "Error: Durp?\n"); 748 | return 1; 749 | } 750 | return 0; 751 | } 752 | --------------------------------------------------------------------------------