Write vector of unsigned char to binary file c++ - c++

I am reading binary file cmd.exe into unsigned chars array. Total bytes read into bytes_read are 153. I converted it to base64 string and then decode this string back (code from 2nd answer base64 decode snippet in c++) into vector<'BYTE>. Here BYTE is unsigned char.
decodedData.size() is also 153. But when I write this vector to file in binary mode to get my cmd.exe file again I get only 1 KB file. What thing I missed?
// Reading size of file
FILE * file = fopen("cmd.exe", "r+");
if (file == NULL) return 1;
fseek(file, 0, SEEK_END);
long int size = ftell(file);
fclose(file);
// Reading data to array of unsigned chars
file = fopen("cmd.exe", "r+");
unsigned char * myData = (unsigned char *)malloc(size);
int bytes_read = fread(myData, sizeof(unsigned char), size, file);
fclose(file);
std::string encodedData = base64_encode(&myData[0], bytes_read);
std::vector<BYTE> decodedData = base64_decode(encodedData);
////write data to file
ofstream outfile("cmd.exe", ios::out | ios::binary);
outfile.write((const char *)decodedData.data(), decodedData.size());
Update:
Thanks #chux for suggesting "r+" --> "rb+" Problem resolved.

You marked this as C++.
This is one C++ approach using fstream to read a binary file. To simplify for this example, I created a somewhat bigger m_buff than needed. From the comments, it sounds like your fopen("cmd.exe", "r+") was in error, so I'm only providing a C++ binary read.
Method tReader() a) opens a file in binary mode, b) reads the data into m_buff, and c) captures gCount for display.
It also demonstrates one possible use of chrono to measure duration.
#include <chrono>
// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock HRClk_t;
typedef HRClk_t::time_point Time_t;
typedef std::chrono::microseconds US_t;
using namespace std::chrono_literals; // suffixes 100ms, 2s, 30us
#include <iostream>
#include <fstream>
#include <cassert>
class T516_t
{
enum BuffConstraints : uint32_t {
Meg = (1024 * 1024),
END_BuffConstraints
};
char* m_buff;
int64_t m_gCount;
public:
T516_t()
: m_buff (nullptr)
, m_gCount (0)
{
m_buff = new char[Meg];
}
~T516_t() = default;
int exec()
{
tReader();
return(0);
}
private: // methods
void tReader()
{
std::string pfn = "/home/dmoen/.wine/drive_c/windows/system32/cmd.exe";
// open file in binary mode
std::ifstream sIn (pfn, std::ios_base::binary);
if (!sIn.is_open()) {
std::cerr << "UNREACHABLE: unable to open sIn " << pfn
<< " priviledges? media offline?";
return;
}
Time_t start_us = HRClk_t::now();
do
{
// perform read
sIn.read (m_buff, Meg);
// If the input sequence runs out of characters to extract (i.e., the
// end-of-file is reached) before n characters have been successfully
// read, buff contains all the characters read until that point, and
// both eofbit and failbit flags are set
m_gCount = sIn.gcount();
if(sIn.eof()) { break; } // exit when no more data
if(sIn.failbit ) {
std::cerr << "sIn.faileBit() set" << std::endl;
}
}while(1);
auto duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
sIn.close();
std::cout << "\n " << pfn
<< " " << m_gCount << " bytes"
<< " " << duration_us.count() << " us"
<< std::endl;
} // int64_t tReader()
}; // class T516_t
int main(int , char**)
{
Time_t start_us = HRClk_t::now();
int retVal = -1;
{
T516_t t516;
retVal = t516.exec();
}
auto duration_us = std::chrono::duration_cast<US_t>(HRClk_t::now() - start_us);
std::cout << " FINI " << duration_us.count() << " us" << std::endl;
return(retVal);
}
One typical output on my system looks like:
/home/dmoen/.wine/drive_c/windows/system32/cmd.exe 722260 bytes 1180 us
FINI 1417 us
Your results will vary.
Your ofstream use looks good (so did not replicate).

Related

C++ Convert char array to string array

I have next C++ code in VS2019 under Windows 10:
char const* const fileName = "random_StringArray_10000000";
FILE* infile;
long fileSize;
char* buffer;
size_t readBytes;
infile = fopen(fileName, "rb");
if (infile == NULL)
{
fputs("File error", stderr); exit(1);
}
fseek(infile, 0, SEEK_END);
fileSize = ftell(infile);
rewind(infile);
buffer = (char*)malloc(sizeof(char) * fileSize);
if (buffer == NULL)
{
fputs("Memory error", stderr); exit(2);
}
auto start = chrono::steady_clock::now();
readBytes = fread(buffer, 1, fileSize, infile);
auto end = chrono::steady_clock::now();
if (readBytes != fileSize)
{
fputs("Reading error", stderr); exit(3);
}
fclose(infile);
free(buffer);
auto elapsed_ms = chrono::duration_cast<chrono::milliseconds>(end - start);
cout << "Elapsed ms: " << elapsed_ms.count() << endl;
cout << "String count: " << stringCount << endl;
system("pause");
return 0;
This method used because it is fastest way to read file from disk under VS2019.
Now i need to convert char array to the string array.
random_StringArray_10000000 - UTF8 text file.
Strings lenght 8 - 120 symbols.
Hex view of this file:
0x0D 0x0A separate strings.
Which fastest way to convert char array (buffer) to the C++ string array?
There seems to be a regularity to your data, all strings are eight characters long and separated by the same two characters. With that in mind the following seems fairly fast.
size_t arraySize = readBytes/10;
std::string* array = new std::string[arraySize];
for (size_t i = 0; i < arraySize; ++i)
array[i].assign(buffer + 10*i, 8);
Of course timing is necessary to be sure what is fastest.
Reading lines of text from a file is much simpler if you use the classes from the c++ standard library.
This should be all of the code you need:
#include <fstream>
#include <vector>
#include <string>
#include <iostream>
int main()
{
char const* const fileName = "random_StringArray_10000000";
std::ifstream in(fileName);
if (!in)
{
std::cout << "File error\n";
return 1;
}
std::vector<std::string> lines;
std::string line;
while (std::getline(in, line))
{
lines.push_back(std::move(line));
}
return 0;
}
char as[ ] = "a char array"; : is a char array
char const* const fileName = "random_StringArray_10000000"; : is a c string
This is also a c string:
char* cs = const_cast<char*>( fileName );
If you want std::string use:
std::string s(as);
I wasn't sure which string conversion you wanted so I just added what I could off the top of my head. But here's a compilable example too.

Concurrent appends to a file : writes getting lost

Are concurrent appends to a file support feature?
I tested this with concurrent threads + fstream for each thread. I see that data is not corrupt, but some writes are lost.
The file size is less than expected after the writes finish. Writes don't overlap.
If I write with custom seeks with each fstream where I coordinate which offset each thread will write, no writes are lost.
Here is the sample code :
#include <fstream>
#include <vector>
#include <thread>
#include "gtest/gtest.h"
void append_concurrently(string filename, const int data_in_gb, const int num_threads, const char start_char,
bool stream_cache = true) {
const int offset = 1024;
const long long num_records_each_thread = (data_in_gb * 1024 * ((1024 * 1024) / (num_threads * offset)));
{
auto write_file_fn = [&](int index) {
// each thread has its own handle
fstream file_handle(filename, fstream::app | fstream::binary);
if (!stream_cache) {
file_handle.rdbuf()->pubsetbuf(nullptr, 0); // no bufferring in fstream
}
vector<char> data(offset, (char)(index + start_char));
for (long long i = 0; i < num_records_each_thread; ++i) {
file_handle.write(data.data(), offset);
if (!file_handle) {
std::cout << "File write failed: "
<< file_handle.fail() << " " << file_handle.bad() << " " << file_handle.eof() << std::endl;
break;
}
}
// file_handle.flush();
};
auto start_time = chrono::high_resolution_clock::now();
vector<thread> writer_threads;
for (int i = 0; i < num_threads; ++i) {
writer_threads.push_back(std::thread(write_file_fn, i));
}
for (int i = 0; i < num_threads; ++i) {
writer_threads[i].join();
}
auto end_time = chrono::high_resolution_clock::now();
std::cout << filename << " Data written : " << data_in_gb << " GB, " << num_threads << " threads "
<< ", cache " << (stream_cache ? "true " : "false ") << ", size " << offset << " bytes ";
std::cout << "Time taken: " << (end_time - start_time).count() / 1000 << " micro-secs" << std::endl;
}
{
ifstream file(filename, fstream::in | fstream::binary);
file.seekg(0, ios_base::end);
// This EXPECT_EQ FAILS as file size is smaller than EXPECTED
EXPECT_EQ(num_records_each_thread * num_threads * offset, file.tellg());
file.seekg(0, ios_base::beg);
EXPECT_TRUE(file);
char data[offset]{ 0 };
for (long long i = 0; i < (num_records_each_thread * num_threads); ++i) {
file.read(data, offset);
EXPECT_TRUE(file || file.eof()); // should be able to read until eof
char expected_char = data[0]; // should not have any interleaving of data.
bool same = true;
for (auto & c : data) {
same = same && (c == expected_char) && (c != 0);
}
EXPECT_TRUE(same); // THIS PASSES
if (!same) {
std::cout << "corruption detected !!!" << std::endl;
break;
}
if (file.eof()) { // THIS FAILS as file size is smaller
EXPECT_EQ(num_records_each_thread * num_threads, i + 1);
break;
}
}
}
}
TEST(fstream, file_concurrent_appends) {
string filename = "file6.log";
const int data_in_gb = 1;
{
// trunc file before write threads start.
{
fstream file(filename, fstream::in | fstream::out | fstream::trunc | fstream::binary);
}
append_concurrently(filename, data_in_gb, 4, 'B', false);
}
std::remove(filename.c_str());
}
Edit:
I moved fstream to be shared by all threads. Now, for 512 byte buffer size, i see 8 writes totalling 4 KB lost consistently.
const int offset = 512;
const long long num_records_each_thread = (data_in_gb * 1024 * ((1024 * 1024) / (num_threads * offset)));
fstream file_handle(filename, fstream::app | fstream::binary);
if (!stream_cache) {
file_handle.rdbuf()->pubsetbuf(nullptr, 0); // no bufferring in fstream
}
Problem does not reproduce with 4KB buffer size.
Running main() from gtest_main.cc
Note: Google Test filter = *file_conc*_*append*
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from fstream
[ RUN ] fstream.file_concurrent_appends
file6.log Data written : 1 GB, 1 threads , cache true , size 512 bytes Time taken: 38069289 micro-secs
d:\projs\logpoc\tests\test.cpp(279): error: Expected: num_records_each_thread * num_threads * offset
Which is: 1073741824
To be equal to: file.tellg()
Which is: 1073737728
d:\projs\logpoc\tests\test.cpp(301): error: Expected: num_records_each_thread * num_threads
Which is: 2097152
To be equal to: i + 1
Which is: 2097145
Edit 2:
Close file_handle after joining all threads to flush the data from the internal buffer. This resolved above issue.
According to §29.4.2 ¶7 of the official ISO C++20 standard, the functions provided by std::fstream are generally thread-safe.
However, if every thread has its own std::fstream object, then, as far as the C++ standard library is concerned, these are distinct streams and no synchronization will take place. Only the operating system's kernel will be aware that all file handles point to the same file. Therefore, any synchronization will have to be done by the kernel. But the kernel possibly isn't even aware that a write is supposed to go to the end of the file. Depending on your platform, it is possible that the kernel only receives write requests for certain file positions. If the end of file has meanwhile been moved by an append from another thread, then the position for a thread's previous write request may no longer be to the end of the file.
According to the documentation on std::fstream::open, opening a file in append mode will cause the stream to seek to the end of the file before every write. This behavior seems to be exactly what you want. But, for the reasons stated above, this will probably only work if all threads share the same std::fstream object. In that case, the std::fstream object should be able to synchronize all writes. In particular, it should be able to perform the seeks to the end of file and the subsequent writes atomically.

stringstream doesn't clean up completely?

I'm working on what will be a pretty large and complicated system and trying to make sure it's as watertight as possible right from the start. Whilst running some memory checks, I noticed something odd when using stringstreams: they don't always seem to release all the memory when they get deleted/go out of scope.
I've tried searching the internet for answers, but most are old (so possibly out of date) and/or are more concerned with refreshing the contents than releasing the memory, so I've not really been able to tell if it's a known issue or a common mistake I'm making.
I've written a simple test to show what's going on:
#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
using namespace std;
float getMemUsage(int& pid)
{
if (pid < 0)
pid = getpid();
char buf[30];
snprintf(buf, 30, "/proc/%u/statm", (unsigned)pid);
FILE* pf = fopen(buf, "r");
if (pf)
{
unsigned size; // total program size
//unsigned resident;// resident set size
//unsigned share;// shared pages
//unsigned text;// text (code)
//unsigned lib;// library
//unsigned data;// data/stack
//unsigned dt;// dirty pages (unused in Linux 2.6)
fscanf(pf, "%u" /* %u %u %u %u %u"*/, &size/*, &resident, &share, &text, &lib, &data*/);
fclose(pf);
return size/1024.0;
}
else
return -1.0;
}
int main(int argc, char* argv[])
{
if (argc < 2)
cerr << "no file specified\n";
ifstream file;
file.open(argv[1]);
int pid = -1;
const float memUseAtStart = getMemUsage(pid);
{
float memUseBefore = getMemUsage(pid);
stringstream sstream;
float memUseAfter = getMemUsage(pid);
cerr << "\tMemory use change after stringstream declaration: " << memUseAfter - memUseBefore << endl;
memUseBefore = getMemUsage(pid);
filebuf* pbuf = file.rdbuf();
memUseAfter = getMemUsage(pid);
cerr << "\tMemory use change after getting file buffer: " << memUseAfter - memUseBefore << endl;
memUseBefore = getMemUsage(pid);
sstream << pbuf;
memUseAfter = getMemUsage(pid);
cerr << "\tMemory use change after copying file contents: " << memUseAfter - memUseBefore << endl;
memUseBefore = getMemUsage(pid);
sstream.clear();
sstream.str( string() );
memUseAfter = getMemUsage(pid);
cerr << "\tMemory use change after 'clearing': " << memUseAfter - memUseBefore << endl;
}
cerr << "Overall memory use change: " << getMemUsage(pid) - memUseAtStart << endl;
file.close();
return 0;
}
Which gives me the following output when called with a file larger than around 32K:
Memory use change after stringstream declaration: 0
Memory use change after getting file buffer: 0
Memory use change after copying file contents: 0.0322266
Memory use change after 'clearing': 0
Overall memory use change: 0.00195312
I'm running on Linux (SL6.6) and compiling with gcc 4.1.2 (though I've also tried clang and ICC with similar results).
Obviously, it's not a huge leak; it's just a little annoying that I can't make it completely tidy... Is there something I can/should do to release the memory manually? Or is it just something weird (with my setup and/or stringstream itself) I'll have to live with?
NB The intended use for the stringstream is to read in some file contents above and then parse them line by line; I would try using istringstream but I couldn't figure out how to set its value from the ifstream...
Thanks!

Processing large files with libsndfile

Currently I've written a program (in Visual Studio C++) based on the available example code of "libsndfile" in which I've just added a subtraction-function to subtract one (channel of a) WAV-file with another.
The program worked perfectly for files with a limit of 4 channels.
The problem is that as soon as I upload a file with as many as 5 channels or more, the program refuses to execute the function. This is independent of the frame-size as I've managed to process 4-channel files which were many times larger than some 5/6-channel-files which I have used.
Has anyone encountered this or a similar problem before? If requested I will provide the source code.
My apologies for the bad structure, I don't really optimize until I get the whole deal working.
Code:
#include <sndfile.hh>
#include <tinyxml2.h>
#include <iostream>
#include <fstream>
#define BLOCK_SIZE 32
using TiXmlDocument = tinyxml2::XMLDocument;
using CStdStringA = std::string;
int main(int argc, char* argv[])
{
SNDFILE *infile = NULL;
SNDFILE *infile2 = NULL;
SNDFILE *outfile = NULL;
SF_INFO sfinfo;
SF_INFO sfinfo2;
SF_INFO sfinfoOut;
sf_count_t readcount;
//int filetype = SF_FORMAT_WAV | SF_FORMAT_PCM_24; // 24-bit Wav-Files
TiXmlDocument iDoc; // Init tinyXML
int i; // Iteration-var;
short BufferIn[BLOCK_SIZE]; // Buffer1
short BufferIn2[BLOCK_SIZE]; // Buffer2
short BufferOut[BLOCK_SIZE];
CStdStringA FileLoc, FileName, FileLocWav, FileLocTxt,FileLocRaw; // OutputFile locationstring
CStdStringA WavName, WavName2, FileIn, FileIn2;
FileLoc = "C:\\Testfiles\\";
//TextTitle(FileLoc); // Print console-header
printf("Name Wavefile 1:\n");
std::cin >> WavName;
FileIn = FileLoc + WavName + ".wav";
if((infile = sf_open(FileIn.c_str(),SFM_READ,&sfinfo)) == NULL) // Open Wav-file 2 instance
{
printf("Not able to open input file 1\n");
printf("\n\nPress any key to exit."); // Exit-text if file not present
puts(sf_strerror(NULL)) ;
return 1;
}
std::cout << "Wav file 1 succesfully opened." << std::endl;
std::cout << "\nChannels: " << sfinfo.channels << "\nFrames: " << sfinfo.frames << std::endl; // Print Channels & Frames
std::cout << "Samplerate: " << sfinfo.samplerate << "\n\n" << std::endl;// Print Samplerate
printf("Name Wavefile 2:\n");
std::cin >> WavName2;
FileIn2 = FileLoc + WavName2 + ".wav";
if((infile2 = sf_open(FileIn2.c_str(),SFM_READ,&sfinfo2)) == NULL) // Open Wav-file 2 instance
{
printf("Not able to open input file 2\n");
printf("\n\nPress any key to exit."); // Exit-text if file not present
puts(sf_strerror(NULL)) ;
return 1;
}
std::cout << "Wav file 2 succesfully opened." << std::endl;
std::cout << "\nChannels: " << sfinfo2.channels << "\nFrames: " << sfinfo2.frames << std::endl; // Print Channels & Frames
std::cout << "Samplerate: " << sfinfo2.samplerate << "\n\n" << std::endl;// Print Samplerate
//{
//std::cout << "Format differs from eachother, abort program.";
//printf("\n\nPress any key to exit."); // Exit-text
//return 1;
//}
if(sfinfo.channels != sfinfo2.channels) // Abort if channels not the same
{
std::cout << "Channelammount differs from eachother, abort program.";
printf("\n\nPress any key to exit."); // Exit-text
return 1;
}
if(sfinfo.samplerate != sfinfo2.samplerate) // Abort if samplerate not the same
{
std::cout << "Samplerate differs from eachother, abort program.";
printf("\n\nPress any key to exit."); // Exit-text
return 1;
}
std::cout << "Give a filename for Txt- & Wav-file: ";
std::cin >> FileName;
FileLoc += FileName; // Fuse Filelocation with given name
FileLocTxt = FileLoc + ".txt";
FileLocWav = FileLoc + ".wav";
FileLocRaw = FileLoc + "Raw.txt";
sfinfoOut.channels = sfinfo.channels;
sfinfoOut.format = sfinfo.format;
sfinfoOut.samplerate = sfinfo.samplerate;
if((outfile = sf_open(FileLocWav.c_str(),SFM_WRITE,&sfinfoOut)) == NULL) // Open Wav-file 2 instance
{
printf("Not able to create output file \n");
printf("\n\nPress any key to exit."); // Exit-text if file not present
puts(sf_strerror(NULL)) ;
return 1;
}
std::ofstream myfile;
myfile.open(FileLocTxt.c_str(),std::ios::app);
std::ofstream myfileRaw;
myfileRaw.open(FileLocRaw.c_str(),std::ios::app);
while((readcount = sf_read_short(infile, BufferIn, BLOCK_SIZE))) // While there are still frames to be processed
{
//process_data (data, readcount, sfinfo.channels) ;
auto readcount2 = sf_read_short(infile2, BufferIn2, BLOCK_SIZE);
for(i = 0; i < BLOCK_SIZE; i++) // BLOCK_SIZE decides the chunk-size
{
BufferOut[i] = BufferIn[i] - BufferIn2[i];
myfileRaw << BufferOut[i];
}
sf_write_short(outfile, BufferOut, BLOCK_SIZE) ; // Write the data to a new file
}
sf_close(infile); // Close Wav-file handlers
sf_close(infile2);
sf_close(outfile);
myfile.close(); // Close text-file handlers
printf("\n\nPress any key to exit."); // Exit-text
return 0;
}
The documentation states:
File Read Functions (Items)
sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
The file read items functions fill the array pointed to by ptr with the requested number of items. The items parameter must be an integer product of the number of channels or an error will occur
There's your problem, since BLOCK_SIZE is a multiple of 1,2,4 but not of 3 and 5 - so that would fail.
About the problem that program completes as if calculations had taken place: add appropriate error handling.

How can I get the duration of an MP3 file (CBR or VBR) with a very small library or native code c/c++?

I can't use any mp3 code that is patented by Fraunhofer, so no encoders OR decoders (e.g. ffmpeg, lame, MAD, etc.), plus it's too big.
I am doing this on Windows, but DirectShow's IMediaDet seems to slow down over time, calling it a few hundred times brings my system to a crawl, even re-using the same interface object and just putting the file name and getting duration!
So, is there some code out there that can read VBR files with C/C++ and get the duration?
There was another post on here to do CBR in C++, but the code makes a ton of assumptions and wont work for VBR of course.
Most MP3 files have an ID3 header. It is not hard to decode that and get the duration.
Here is some very basic and ugly code that illustrates the technique.
#include <iostream>
#include <iomanip>
size_t GetMP3Duration(const std::string sFileName);
int main(int argc, char* argv[])
{
try
{
size_t nLen = GetMP3Duration(argv[1]);
if (nLen==0)
{
std::cout << "Not Found" << std::endl;
}
else
{
std::cout << nLen << " miliseconds" << std::endl;
std::cout << nLen/60000 << ":";
nLen %= 60000;
std::cout << nLen/1000 << ".";
std::cout << std::setw(3) << std::setfill('0') << nLen%1000 << std::endl;
}
}
catch (std::exception &e)
{
std::cout << "Exception: " << e.what() << std::endl;
}
return 0;
}
#include <cstring>
#include <vector>
#include <iostream>
#include <fstream>
#include <cctype>
#include <cstdlib>
unsigned DecodeMP3SafeInt(unsigned nVal)
{
// nVal has 4 bytes (8-bits each)
// - discard most significant bit from each byte
// - reverse byte order
// - concatenate the 4 * 7-bit nibbles into a 24-bit size.
unsigned char *pValParts = reinterpret_cast<unsigned char *>(&nVal);
return (pValParts[3] & 0x7F) |
((pValParts[2] & 0x7F) << 7) |
((pValParts[1] & 0x7F) << 14) |
((pValParts[0] & 0x7F) << 21);
}
#pragma pack(1)
struct MP3Hdr {
char tag[3];
unsigned char maj_ver;
unsigned char min_ver;
unsigned char flags;
unsigned int size;
};
struct MP3ExtHdr {
unsigned int size;
unsigned char num_flag_bytes;
unsigned char extended_flags;
};
struct MP3FrameHdr {
char frame_id[4];
unsigned size;
unsigned char flags[2];
};
#pragma pack()
size_t GetMP3Duration(const std::string sFileName)
{
std::ifstream fin(sFileName.c_str(), std::ifstream::binary);
if (!fin)
throw std::invalid_argument("Cannot open file");
// Read Header
MP3Hdr hdr = { 0 };
fin.read(reinterpret_cast<char *>(&hdr), sizeof(hdr));
if (!fin.good())
throw std::invalid_argument("Error reading file");
if (0 != ::memcmp(hdr.tag, "ID3", 3))
throw std::invalid_argument("Not an MP3 File");
// Read extended header, if present
if (0 != (hdr.flags&0x40))
{
fin.seekg(sizeof(MP3ExtHdr), std::ifstream::cur);
if (!fin.good())
throw std::invalid_argument("Error reading file");
}
// read a chunk of file.
const size_t nDefaultSize(2048);
std::vector<char> vBuff(nDefaultSize);
fin.read(&vBuff[0], vBuff.size());
size_t nSize = fin.gcount();
if (!nSize)
throw std::invalid_argument("Error reading file");
vBuff.resize(nSize);
size_t nUsed = 0;
while (nSize-nUsed > sizeof(MP3FrameHdr))
{
MP3FrameHdr *pFrame = reinterpret_cast<MP3FrameHdr *>(&vBuff[nUsed]);
nUsed += sizeof(MP3FrameHdr);
size_t nDataLen = DecodeMP3SafeInt(pFrame->size);
if (nDataLen > (nSize-nUsed))
throw std::invalid_argument("Corrupt file");
if (!::isupper(pFrame->flags[0])) // past end of tags
return 0;
if (0 == ::memcmp(pFrame->frame_id, "TLEN", 4))
{
// skip an int
nUsed += sizeof(int);
// data is next
return atol(&vBuff[nUsed]);
}
else
{
nUsed += nDataLen;
}
}
return 0;
}
Jeff,
the only valid way is to go through whole mp3 file, find every mp3 frame inside of it and compute total duration for them.
Main characteristic of mp3 file is that their density might differ, and also that lot's of other binary data could be included inside of it. ID3 tags for example, that any decoder will skip upon reading.
Anyway - look here for mp3 frame header info:
http://www.mp3-converter.com/mp3codec/mp3_anatomy.htm
try to create code that will correctly parse header by header, calculate their duration (from sampling frequency) and then total the durations for all frames.
You don't have to decode the frames, just use headers from them.
If you don't mind LGPL try http://sourceforge.net/projects/mpg123net/
I found a library that does it, LGPL v3: http://www.codeproject.com/KB/audio-video/mpegaudioinfo.aspx
How about tagLib or id3lib?
They are not decoders per se, they are more of extracting the track/artist/album and host of other information that will enable you to do what you need to do...