static status WriteCues (AFfilehandle file) { int i, *markids, markCount; uint32_t numCues, cueChunkSize, listChunkSize; _WAVEInfo *wave; assert(file); markCount = afGetMarkIDs(file, AF_DEFAULT_TRACK, NULL); if (markCount == 0) return AF_SUCCEED; wave = file->formatSpecific; if (wave->markOffset == 0) wave->markOffset = af_ftell(file->fh); else af_fseek(file->fh, wave->markOffset, SEEK_SET); af_fwrite("cue ", 4, 1, file->fh); /* The cue chunk consists of 4 bytes for the number of cue points followed by 24 bytes for each cue point record. */ cueChunkSize = 4 + markCount * 24; af_write_uint32_le(&cueChunkSize, file->fh); numCues = markCount; af_write_uint32_le(&numCues, file->fh); markids = _af_calloc(markCount, sizeof (int)); assert(markids != NULL); afGetMarkIDs(file, AF_DEFAULT_TRACK, markids); /* Write each marker to the file. */ for (i=0; i < markCount; i++) { uint32_t identifier, position, chunkStart, blockStart; uint32_t sampleOffset; AFframecount markposition; identifier = markids[i]; af_write_uint32_le(&identifier, file->fh); position = i; af_write_uint32_le(&position, file->fh); /* For now the RIFF id is always the first data chunk. */ af_fwrite("data", 4, 1, file->fh); /* For an uncompressed WAVE file which contains only one data chunk, chunkStart and blockStart are zero. */ chunkStart = 0; af_fwrite(&chunkStart, sizeof (uint32_t), 1, file->fh); blockStart = 0; af_fwrite(&blockStart, sizeof (uint32_t), 1, file->fh); markposition = afGetMarkPosition(file, AF_DEFAULT_TRACK, markids[i]); /* Sample offsets are stored in the WAVE file as frames. */ sampleOffset = markposition; af_write_uint32_le(&sampleOffset, file->fh); } /* Now write the cue names which is in a master list chunk with a subchunk for each cue's name. */ listChunkSize = 4; for (i=0; i<markCount; i++) { const char *name; name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]); /* Each label chunk consists of 4 bytes for the "labl" chunk ID, 4 bytes for the chunk data size, 4 bytes for the cue point ID, and then the length of the label as a Pascal-style string. In all, this is 12 bytes plus the length of the string, its size byte, and a trailing pad byte if the length of the chunk is otherwise odd. */ listChunkSize += 12 + (strlen(name) + 1) + ((strlen(name) + 1) % 2); } af_fwrite("LIST", 4, 1, file->fh); af_write_uint32_le(&listChunkSize, file->fh); af_fwrite("adtl", 4, 1, file->fh); for (i=0; i<markCount; i++) { const char *name; uint32_t labelSize, cuePointID; name = afGetMarkName(file, AF_DEFAULT_TRACK, markids[i]); /* Make labelSize even if it is not already. */ labelSize = 4+(strlen(name)+1) + ((strlen(name) + 1) % 2); cuePointID = markids[i]; af_fwrite("labl", 4, 1, file->fh); af_write_uint32_le(&labelSize, file->fh); af_write_uint32_le(&cuePointID, file->fh); af_fwrite(name, strlen(name) + 1, 1, file->fh); /* If the name plus the size byte comprises an odd length, add another byte to make the string an even length. */ if (((strlen(name) + 1) % 2) != 0) { uint8_t zero=0; af_write_uint8(&zero, file->fh); } } free(markids); return AF_SUCCEED; }
status WAVEFile::writeCues() { int *markids, markCount; uint32_t numCues, cueChunkSize, listChunkSize; markCount = afGetMarkIDs(this, AF_DEFAULT_TRACK, NULL); if (markCount == 0) return AF_SUCCEED; if (markOffset == 0) markOffset = fh->tell(); else fh->seek(markOffset, File::SeekFromBeginning); fh->write("cue ", 4); /* The cue chunk consists of 4 bytes for the number of cue points followed by 24 bytes for each cue point record. */ cueChunkSize = 4 + markCount * 24; writeU32(&cueChunkSize); numCues = markCount; writeU32(&numCues); markids = (int *) _af_calloc(markCount, sizeof (int)); assert(markids != NULL); afGetMarkIDs(this, AF_DEFAULT_TRACK, markids); /* Write each marker to the file. */ for (int i=0; i < markCount; i++) { uint32_t identifier, position, chunkStart, blockStart; uint32_t sampleOffset; AFframecount markposition; identifier = markids[i]; writeU32(&identifier); position = i; writeU32(&position); /* For now the RIFF id is always the first data chunk. */ fh->write("data", 4); /* For an uncompressed WAVE file which contains only one data chunk, chunkStart and blockStart are zero. */ chunkStart = 0; fh->write(&chunkStart, sizeof (uint32_t)); blockStart = 0; fh->write(&blockStart, sizeof (uint32_t)); markposition = afGetMarkPosition(this, AF_DEFAULT_TRACK, markids[i]); /* Sample offsets are stored in the WAVE file as frames. */ sampleOffset = markposition; writeU32(&sampleOffset); } /* Now write the cue names which is in a master list chunk with a subchunk for each cue's name. */ listChunkSize = 4; for (int i=0; i<markCount; i++) { const char *name; name = afGetMarkName(this, AF_DEFAULT_TRACK, markids[i]); /* Each label chunk consists of 4 bytes for the "labl" chunk ID, 4 bytes for the chunk data size, 4 bytes for the cue point ID, and then the length of the label as a Pascal-style string. In all, this is 12 bytes plus the length of the string, its size byte, and a trailing pad byte if the length of the chunk is otherwise odd. */ listChunkSize += 12 + (strlen(name) + 1) + ((strlen(name) + 1) % 2); } fh->write("LIST", 4); writeU32(&listChunkSize); fh->write("adtl", 4); for (int i=0; i<markCount; i++) { const char *name; uint32_t labelSize, cuePointID; name = afGetMarkName(this, AF_DEFAULT_TRACK, markids[i]); /* Make labelSize even if it is not already. */ labelSize = 4+(strlen(name)+1) + ((strlen(name) + 1) % 2); cuePointID = markids[i]; fh->write("labl", 4); writeU32(&labelSize); writeU32(&cuePointID); fh->write(name, strlen(name) + 1); /* If the name plus the size byte comprises an odd length, add another byte to make the string an even length. */ if (((strlen(name) + 1) % 2) != 0) { uint8_t zero=0; writeU8(&zero); } } free(markids); return AF_SUCCEED; }
int testmarkers (int fileformat) { AFfilehandle file; AFfilesetup setup; int markids[] = {1, 2, 3, 4}; AFframecount markpositions[] = {14, 54, 23, 101}; const char *marknames[] = {"one", "two", "three", "four"}; short frames[FRAME_COUNT * 2] = {0}; int readmarkcount; int readmarkids[4]; AFframecount frameswritten; setup = afNewFileSetup(); ensure(setup != AF_NULL_FILESETUP, "Could not create file setup"); afInitFileFormat(setup, fileformat); afInitChannels(setup, AF_DEFAULT_TRACK, 2); afInitMarkIDs(setup, AF_DEFAULT_TRACK, markids, 4); afInitMarkName(setup, AF_DEFAULT_TRACK, markids[0], marknames[0]); afInitMarkName(setup, AF_DEFAULT_TRACK, markids[1], marknames[1]); afInitMarkName(setup, AF_DEFAULT_TRACK, markids[2], marknames[2]); afInitMarkName(setup, AF_DEFAULT_TRACK, markids[3], marknames[3]); file = afOpenFile(sTestFileName, "w", setup); ensure(file != AF_NULL_FILEHANDLE, "Could not open file for writing"); afFreeFileSetup(setup); frameswritten = afWriteFrames(file, AF_DEFAULT_TRACK, frames, FRAME_COUNT); ensure(frameswritten == FRAME_COUNT, "Error writing audio data"); afSetMarkPosition(file, AF_DEFAULT_TRACK, markids[0], markpositions[0]); afSetMarkPosition(file, AF_DEFAULT_TRACK, markids[1], markpositions[1]); afSetMarkPosition(file, AF_DEFAULT_TRACK, markids[2], markpositions[2]); afSetMarkPosition(file, AF_DEFAULT_TRACK, markids[3], markpositions[3]); afCloseFile(file); file = afOpenFile(sTestFileName, "r", NULL); ensure(file != AF_NULL_FILEHANDLE, "Could not open file for reading"); readmarkcount = afGetMarkIDs(file, AF_DEFAULT_TRACK, NULL); ensure(readmarkcount == 4, "Number of markers is not correct"); afGetMarkIDs(file, AF_DEFAULT_TRACK, readmarkids); for (int i=0; i<readmarkcount; i++) ensure(readmarkids[i] = markids[i], "Marker identification numbers do not match"); for (int i=0; i<readmarkcount; i++) { AFframecount readmarkposition; const char *readmarkname; readmarkposition = afGetMarkPosition(file, AF_DEFAULT_TRACK, readmarkids[i]); readmarkname = afGetMarkName(file, AF_DEFAULT_TRACK, readmarkids[i]); ensure(readmarkposition == markpositions[i], "Marker positions do not match"); ensure(strcmp(readmarkname, marknames[i]) == 0, "Marker names do not match"); } afCloseFile(file); return EXIT_SUCCESS; }