#include #include #include #include #define SPEED_MULTIPLIER_MINECRAFT 0.9999781870670353 #define SPEED_MULTIPLIER_OLDFRAPS 0.999966660584269 // For Windows 10 (new) #define SPEED_MULTIPLIER_DEFAULT 1.0000255090380563 // For Windows 10 (old) //#define SPEED_MULTIPLIER_DEFAULT 1.0000075912646884 // For Windows 7 //#define SPEED_MULTIPLIER_DEFAULT 0.999965272464057 #define SPEED_MULTIPLIER_STARTECH 0.9999878877297875 #define BUFFER_SIZE 16384 static unsigned int parseUInt32LE(const char * value) { return ((unsigned int) value[0] & 0xFF) | (((unsigned int) value[1] & 0xFF) << 8) | (((unsigned int) value[2] & 0xFF) << 16) | (((unsigned int) value[3] & 0xFF) << 24); } static void packUInt32LE(unsigned int value, char * outBuffer) { outBuffer[0] = value & 0xFF; outBuffer[1] = value >> 8 & 0xFF; outBuffer[2] = value >> 16 & 0xFF; outBuffer[3] = value >> 24 & 0xFF; } static unsigned int readChunkHeader(FILE * file, char * outChunkType) { char chunkSize[4]; fread(outChunkType, 1, 4, file); fread(chunkSize, 1, 4, file); return parseUInt32LE(chunkSize); } static bool isChunkType(const char * type, const char * expectedType) { return type[0] == expectedType[0] && type[1] == expectedType[1] && type[2] == expectedType[2] && type[3] == expectedType[3]; } int main(int argc, char ** argv) { size_t originalLength, scaledLength; unsigned int sampleIndex, inBufferIndex, outBufferIndex; double sampleFraction; char wavHeader[46]; char chunkType[4]; unsigned int chunkSize; char inBuffer[BUFFER_SIZE], outBuffer[BUFFER_SIZE]; char sample[2]; double speedMultiplier, speedMultiplierFraction; int argIndex = 1; FILE * inFile, * outFile; unsigned int samplesRepeated = 0, samplesSkipped = 0; if (argc > 1 && !strcmp(argv[1], "-oldfraps")) { speedMultiplier = SPEED_MULTIPLIER_OLDFRAPS; argIndex++; fprintf(stderr, "Using old Fraps multiplier of %f\n", speedMultiplier); } else if (argc > 1 && !strcmp(argv[1], "-minecraft")) { speedMultiplier = SPEED_MULTIPLIER_MINECRAFT; argIndex++; fprintf(stderr, "Using Minecraft+OBS multiplier of %f\n", speedMultiplier); } else if (argc > 1 && !strcmp(argv[1], "-startech")) { speedMultiplier = SPEED_MULTIPLIER_STARTECH; argIndex++; fprintf(stderr, "Using StarTech capture multiplier of %f\n", speedMultiplier); } else { speedMultiplier = SPEED_MULTIPLIER_DEFAULT; fprintf(stderr, "Using default multiplier of %f\n", speedMultiplier); } speedMultiplierFraction = 1.0 / speedMultiplier - 1.0; if (argc - argIndex < 2) { fprintf(stderr, "Error: Must specify input and output files at end of argv\n"); return EXIT_FAILURE; } inFile = fopen(argv[argIndex], "rb"); if (inFile == NULL) { fprintf(stderr, "Error: Couldn't open %s for reading (errno=%d)\n", argv[argIndex], errno); return EXIT_FAILURE; } outFile = fopen(argv[argIndex + 1], "wb"); if (outFile == NULL) { fprintf(stderr, "Error: Couldn't open %s for writing (errno=%d)\n", argv[argIndex + 1], errno); return EXIT_FAILURE; } fread(wavHeader, 1, 36, inFile); if (wavHeader[16] == 0x12) { fseek(inFile, 2, SEEK_CUR); wavHeader[16] = 0x10; } else if (wavHeader[16] != 0x10) { fprintf(stderr, "Error: Unexpected WAV header format ('fmt ' chunk not 0x10 or 0x12 bytes in size, or fmt header not first\n"); return EXIT_FAILURE; } chunkSize = readChunkHeader(inFile, chunkType); while (!isChunkType(chunkType, "data") && !feof(inFile)) { fseek(inFile, chunkSize, SEEK_CUR); chunkSize = readChunkHeader(inFile, chunkType); } if (feof(inFile)) { fprintf(stderr, "Error: Reached EOF without finding 'data' chunk\n"); return EXIT_FAILURE; } originalLength = chunkSize; fwrite(wavHeader, 1, 44, outFile); sampleFraction = 0.0; inBufferIndex = BUFFER_SIZE; outBufferIndex = 0; for (sampleIndex = 0; sampleIndex < originalLength; sampleIndex += 2) { if (inBufferIndex >= BUFFER_SIZE) { inBufferIndex = 0; fread(inBuffer, 1, sampleIndex > originalLength - BUFFER_SIZE ? originalLength % BUFFER_SIZE : BUFFER_SIZE, inFile); } sampleFraction += speedMultiplierFraction; sample[0] = inBuffer[inBufferIndex++]; sample[1] = inBuffer[inBufferIndex++]; if (sampleFraction <= -1.0) { sampleFraction += 1.0; samplesSkipped++; } else { if (outBufferIndex >= BUFFER_SIZE) { fwrite(outBuffer, 1, BUFFER_SIZE, outFile); outBufferIndex -= BUFFER_SIZE; } outBuffer[outBufferIndex++] = sample[0]; outBuffer[outBufferIndex++] = sample[1]; } if (sampleFraction >= 1.0) { if (outBufferIndex >= BUFFER_SIZE) { fwrite(outBuffer, 1, BUFFER_SIZE, outFile); outBufferIndex -= BUFFER_SIZE; } outBuffer[outBufferIndex++] = sample[0]; outBuffer[outBufferIndex++] = sample[1]; sampleFraction -= 1.0; samplesRepeated++; } } fwrite(outBuffer, 1, outBufferIndex, outFile); scaledLength = originalLength + (samplesRepeated - samplesSkipped) * 2; wavHeader[36] = 'd'; wavHeader[37] = 'a'; wavHeader[38] = 't'; wavHeader[39] = 'a'; packUInt32LE(scaledLength, wavHeader + 40); // data chunk size packUInt32LE(scaledLength + 36, wavHeader + 4); // RIFF chunk size fseek(outFile, 0, SEEK_SET); fwrite(wavHeader, 1, 44, outFile); fprintf(stderr, "%u samples repeated, %u samples skipped\n", samplesRepeated, samplesSkipped); return EXIT_SUCCESS; }