void _closeSampleSourceWave(void *sampleSourceDataPtr) { SampleSource sampleSource = (SampleSource)sampleSourceDataPtr; SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; size_t numBytesWritten; RiffChunk chunk; if (sampleSource->openedAs == SAMPLE_SOURCE_OPEN_WRITE) { // Re-open the file for editing fflush(extraData->fileHandle); if (fclose(extraData->fileHandle) != 0) { logError("Could not close WAVE file for finalization"); return; } extraData->fileHandle = fopen(sampleSource->sourceName->data, "rb+"); if (extraData->fileHandle == NULL) { logError("Could not reopen WAVE file for finalization"); return; } // First go to the second chunk in the file and re-read the chunk length if (fseek(extraData->fileHandle, 12, SEEK_SET) != 0) { logError("Could not seek to second chunk during WAVE file finalization"); fclose(extraData->fileHandle); return; } chunk = newRiffChunk(); if (!riffChunkReadNext(chunk, extraData->fileHandle, false)) { logError("Could not read RIFF chunk during WAVE file finalization"); fclose(extraData->fileHandle); freeRiffChunk(chunk); return; } // Go to the next chunk, and then skip the type and write the new length if (fseek(extraData->fileHandle, (long)(chunk->size + 4), SEEK_CUR) != 0) { logError("Could not seek to next chunk during WAVE file finalization"); fclose(extraData->fileHandle); freeRiffChunk(chunk); return; } numBytesWritten = sampleSource->numSamplesProcessed * extraData->bitDepth / 8; if (fwrite(&numBytesWritten, sizeof(unsigned int), 1, extraData->fileHandle) != 1) { logError("Could not write WAVE file size during finalization"); fclose(extraData->fileHandle); freeRiffChunk(chunk); return; } // Add 40 bytes for fmt chunk size and write the RIFF chunk size numBytesWritten += ftell(extraData->fileHandle) - 8; if (fseek(extraData->fileHandle, 4, SEEK_SET) != 0) { logError("Could not seek to fmt chunk during WAVE file finalization"); fclose(extraData->fileHandle); freeRiffChunk(chunk); return; } if (fwrite(&numBytesWritten, sizeof(unsigned int), 1, extraData->fileHandle) != 1) { logError("Could not write WAVE file size in fmt chunk during finalization"); fclose(extraData->fileHandle); freeRiffChunk(chunk); return; } fflush(extraData->fileHandle); fclose(extraData->fileHandle); freeRiffChunk(chunk); } else if (sampleSource->openedAs == SAMPLE_SOURCE_OPEN_READ && extraData->fileHandle != NULL) { fclose(extraData->fileHandle); } }
static boolByte _readWaveFileInfo(const char *filename, SampleSourcePcmData extraData) { int chunkOffset = 0; RiffChunk chunk = newRiffChunk(); boolByte dataChunkFound = false; char format[4]; size_t itemsRead; unsigned int audioFormat; unsigned int byteRate; unsigned int expectedByteRate; unsigned int blockAlign; unsigned int expectedBlockAlign; if (riffChunkReadNext(chunk, extraData->fileHandle, false)) { if (!riffChunkIsIdEqualTo(chunk, "RIFF")) { logFileError(filename, "Invalid RIFF chunk descriptor"); freeRiffChunk(chunk); return false; } // The WAVE file format has two sub-chunks, with the size of both calculated in the size field. Before // either of the subchunks, there are an extra 4 bytes which indicate the format type. We need to read // that before either of the subchunks can be parsed. itemsRead = fread(format, sizeof(byte), 4, extraData->fileHandle); if (itemsRead != 4 || strncmp(format, "WAVE", 4)) { logFileError(filename, "Invalid format description"); freeRiffChunk(chunk); return false; } } else { logFileError(filename, "No chunks following descriptor"); freeRiffChunk(chunk); return false; } if (riffChunkReadNext(chunk, extraData->fileHandle, true)) { if (!riffChunkIsIdEqualTo(chunk, "fmt ")) { logError(filename, "Invalid format chunk header"); freeRiffChunk(chunk); return false; } audioFormat = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); chunkOffset += 2; if (audioFormat != 1) { logError("WAVE file with audio format %d is not supported", audioFormat); freeRiffChunk(chunk); return false; } extraData->numChannels = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); chunkOffset += 2; setNumChannels(extraData->numChannels); extraData->sampleRate = convertByteArrayToUnsignedInt(chunk->data + chunkOffset); chunkOffset += 4; setSampleRate(extraData->sampleRate); byteRate = convertByteArrayToUnsignedInt(chunk->data + chunkOffset); chunkOffset += 4; blockAlign = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); chunkOffset += 2; extraData->bitDepth = (BitDepth) convertByteArrayToUnsignedShort(chunk->data + chunkOffset); if (extraData->bitDepth != kBitDepth16Bit) { logUnsupportedFeature("Non-16-bit files with internal WAVE file support (build with audiofile instead!)"); freeRiffChunk(chunk); return false; } expectedByteRate = (unsigned int)(extraData->sampleRate) * extraData->numChannels * extraData->bitDepth / 8; if (expectedByteRate != byteRate) { logWarn("Possibly invalid bitrate %d, expected %d", byteRate, expectedByteRate); } expectedBlockAlign = (unsigned int)(extraData->numChannels * extraData->bitDepth / 8); if (expectedBlockAlign != blockAlign) { logWarn("Possibly invalid block align %d, expected %d", blockAlign, expectedBlockAlign); } } else { logFileError(filename, "WAVE file has no chunks following format"); freeRiffChunk(chunk); return false; } // We don't need the format data anymore, so free and re-alloc the chunk to avoid a small memory leak freeRiffChunk(chunk); chunk = newRiffChunk(); // FFMpeg (and possibly other programs) have extra sections between the fmt and data chunks. They // can be safely ignored. We just need to find the data chunk. See also: // http://forum.videohelp.com/threads/359689-ffmpeg-Override-Set-ISFT-Metadata while (!dataChunkFound) { if (riffChunkReadNext(chunk, extraData->fileHandle, false)) { if (riffChunkIsIdEqualTo(chunk, "data")) { logDebug("WAVE file has %d bytes", chunk->size); dataChunkFound = true; } else { fseek(extraData->fileHandle, (long) chunk->size, SEEK_CUR); } } else { break; } } if (!dataChunkFound) { logFileError(filename, "Could not find a data chunk. Possibly malformed WAVE file."); freeRiffChunk(chunk); return false; } freeRiffChunk(chunk); return true; }
static boolByte _readWaveFileInfo(const char* filename, SampleSourcePcmData extraData) { int chunkOffset = 0; RiffChunk chunk = newRiffChunk(); char format[4]; size_t itemsRead; unsigned int audioFormat; unsigned int byteRate; unsigned int expectedByteRate; unsigned int blockAlign; unsigned int expectedBlockAlign; if(riffChunkReadNext(chunk, extraData->fileHandle, false)) { if(!riffChunkIsIdEqualTo(chunk, "RIFF")) { logFileError(filename, "Invalid RIFF chunk descriptor"); freeRiffChunk(chunk); return false; } // The WAVE file format has two sub-chunks, with the size of both calculated in the size field. Before // either of the subchunks, there are an extra 4 bytes which indicate the format type. We need to read // that before either of the subchunks can be parsed. itemsRead = fread(format, sizeof(byte), 4, extraData->fileHandle); if(itemsRead != 4 || strncmp(format, "WAVE", 4)) { logFileError(filename, "Invalid format description"); freeRiffChunk(chunk); return false; } } else { logFileError(filename, "No chunks following descriptor"); freeRiffChunk(chunk); return false; } if(riffChunkReadNext(chunk, extraData->fileHandle, true)) { if(!riffChunkIsIdEqualTo(chunk, "fmt ")) { logError(filename, "Invalid format chunk header"); freeRiffChunk(chunk); return false; } audioFormat = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); chunkOffset += 2; if(audioFormat != 1) { logUnsupportedFeature("Compressed WAVE files"); freeRiffChunk(chunk); return false; } extraData->numChannels = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); chunkOffset += 2; setNumChannels(extraData->numChannels); extraData->sampleRate = convertByteArrayToUnsignedInt(chunk->data + chunkOffset); chunkOffset += 4; setSampleRate(extraData->sampleRate); byteRate = convertByteArrayToUnsignedInt(chunk->data + chunkOffset); chunkOffset += 4; blockAlign = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); chunkOffset += 2; extraData->bitsPerSample = convertByteArrayToUnsignedShort(chunk->data + chunkOffset); if(extraData->bitsPerSample > 16) { logUnsupportedFeature("Bitrates greater than 16"); freeRiffChunk(chunk); return false; } else if(extraData->bitsPerSample < 16) { logUnsupportedFeature("Bitrates lower than 16"); freeRiffChunk(chunk); return false; } expectedByteRate = extraData->sampleRate * extraData->numChannels * extraData->bitsPerSample / 8; if(expectedByteRate != byteRate) { logWarn("Possibly invalid bitrate %d, expected %d", byteRate, expectedByteRate); } expectedBlockAlign = extraData->numChannels * extraData->bitsPerSample / 8; if(expectedBlockAlign != blockAlign) { logWarn("Possibly invalid block align %d, expected %d", blockAlign, expectedBlockAlign); } } else { logFileError(filename, "WAVE file has no chunks following format"); freeRiffChunk(chunk); return false; } // We don't need the format data anymore, so free and re-alloc the chunk to avoid a small memory leak freeRiffChunk(chunk); chunk = newRiffChunk(); if(riffChunkReadNext(chunk, extraData->fileHandle, false)) { if(!riffChunkIsIdEqualTo(chunk, "data")) { logFileError(filename, "WAVE file has invalid data chunk header"); freeRiffChunk(chunk); return false; } logDebug("WAVE file has %d bytes", chunk->size); } freeRiffChunk(chunk); return true; }