I am currently using the C++ to implement my code. The idea is I want to read a wav file through a program and then output its waveform on the screen. I have already found some references there:C++ Reading the Data part of a WAV file. and here is my code of reading the audio file. I have no idea of how to generate the waveform .
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
using std::string;
using std::fstream;
typedef struct WAV_HEADER
{
/* for the part of RIEF Chunk Descriptor */
uint8_t RIFF[4]; // RIFF Header Magic header
uint32_t ChunkSize; // RIFF Chunk Size
uint8_t WAVE[4]; // WAVE Header
/* for the part of "fmt" subChunk */
uint8_t fmt[4]; // FMT header
uint32_t Subchunk1Size; // Size of the fmt chunk
uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio
uint32_t SamplesPerSec; // Sampling Frequency in Hz
uint32_t bytesPerSec; // bytes per second
uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo
uint16_t bitsPerSample; // Number of bits per sample
/* For the part of "data" subChunk */
uint8_t Subchunk2ID[4]; // "data" string
uint32_t Subchunk2Size; // Sampled data length
}wav_hdr;
int getFileSize(FILE* inFile);
int main(int argc, char* argv[])
{
wav_hdr wavHeader;
int headerSize = sizeof(wav_hdr), filelength = 0;
const char* filePath;
string input;
if (argc <= 1)
{
cout << "Input wave file name: ";
cin >> input;
cin.get();
filePath = input.c_str();
}
else
{
filePath = argv[1];
cout << "Input wave file name: " << filePath << endl;
}
FILE* wavFile = fopen(filePath, "r");
if (wavFile == nullptr)
{
fprintf(stderr, "Unable to open wave file: %s\n", filePath);
return 1;
}
//Read the header
size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile);
cout << "Header Read " << bytesRead << " bytes." << endl;
if (bytesRead > 0)
{
//Read the data
uint16_t bytesPerSample = wavHeader.bitsPerSample / 8; //Number of bytes per sample
uint64_t numSamples = wavHeader.ChunkSize / bytesPerSample; //How many samples are in the wav file?
static const uint16_t BUFFER_SIZE = 4096;
int8_t* buffer = new int8_t[BUFFER_SIZE];
while ((bytesRead = fread(buffer, sizeof buffer[0], BUFFER_SIZE / (sizeof buffer[0]), wavFile)) > 0)
{
/** DO SOMETHING WITH THE WAVE DATA HERE **/
cout << "Read " << bytesRead << " bytes." << endl;
}
delete [] buffer;
buffer = nullptr;
filelength = getFileSize(wavFile);
cout << "File is :" << filelength << " bytes." << endl;
cout << "RIFF header :" << wavHeader.RIFF[0] << wavHeader.RIFF[1] << wavHeader.RIFF[2] << wavHeader.RIFF[3] << endl;
cout << "WAVE header :" << wavHeader.WAVE[0] << wavHeader.WAVE[1] << wavHeader.WAVE[2] << wavHeader.WAVE[3] << endl;
cout << "FMT :" << wavHeader.fmt[0] << wavHeader.fmt[1] << wavHeader.fmt[2] << wavHeader.fmt[3] << endl;
cout << "Data size :" << wavHeader.ChunkSize << endl;
// Display the sampling Rate from the header
cout << "Sampling Rate :" << wavHeader.SamplesPerSec << endl;
cout << "Number of bits used :" << wavHeader.bitsPerSample << endl;
cout << "Number of channels :" << wavHeader.NumOfChan << endl;
cout << "Number of bytes per second :" << wavHeader.bytesPerSec << endl;
cout << "Data length :" << wavHeader.Subchunk2Size << endl;
cout << "Audio Format :" << wavHeader.AudioFormat << endl;
// Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
cout << "Block align :" << wavHeader.blockAlign << endl;
cout << "Data string :" << wavHeader.Subchunk2ID[0] << wavHeader.Subchunk2ID[1] << wavHeader.Subchunk2ID[2] << wavHeader.Subchunk2ID[3] << endl;
}
fclose(wavFile);
return 0;
}
// find the file size
int getFileSize(FILE* inFile)
{
int fileSize = 0;
fseek(inFile, 0, SEEK_END);
fileSize = ftell(inFile);
fseek(inFile, 0, SEEK_SET);
return fileSize;
}
I would have left this as a comment with code gists and not an answer since it doesn't answer your question directly but I don't have enough reputation for this.
I did what you're trying to do with Qt, most of my code comes from Qt's documentation. You need to find the peak value of the samples and then draw that. Let me share some code that could give you an idea on how you might want to do it.
void Waveform::appendSamples()
{
buffer = audioDecoder->read();
qreal peak = getPeakValue(buffer.format());
const qint16 *data = buffer.constData<qint16>();
int count = buffer.sampleCount() / 2;
for (int i = 0; i < count; i += 1200){ // I want 40 samples per second currently assuming 48kHz
double val = data[i]/peak;
samples.append(val * 300); // *300 for scaling
}
}
qreal Waveform::getPeakValue(const QAudioFormat &format)
{
qreal ret(0);
if (format.isValid()){
switch (format.sampleType()) {
case QAudioFormat::Unknown:
break;
case QAudioFormat::Float:
if (format.sampleSize() != 32)
ret = 0;
else
ret = 1.00003;
break;
case QAudioFormat::SignedInt:
if (format.sampleSize() == 32)
ret = INT_MAX;
else if (format.sampleSize() == 16)
ret = SHRT_MAX;
else if (format.sampleSize() == 8)
ret = CHAR_MAX;
break;
case QAudioFormat::UnSignedInt:
if (format.sampleSize() == 32)
ret = UINT_MAX;
else if (format.sampleSize() == 16)
ret = USHRT_MAX;
else if (format.sampleSize() == 8)
ret = UCHAR_MAX;
break;
default:
break;
}
}
return ret;
}
For the drawing part in Qt (probably not useful to you)
QSGNode* WaveformRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
{
QSGSimpleRectNode *n = static_cast<QSGSimpleRectNode *>(oldNode);
if(!n){
n = new QSGSimpleRectNode();
n->setColor(Qt::red);
}
QVector<double> samples = wav->getSamples(); //retrieve the samples
int numberOfSamples = samples.size();
setItemWidth(qreal(numberOfSamples * 2)); //signal stuff for qml
for(int i = 0; i < numberOfSamples; ++i){
QSGSimpleRectNode *temp = new QSGSimpleRectNode();
temp->setColor(Qt::green);
temp->setRect(i * 2, height()/2 - samples[i], 2, samples[i] * 2);
n->appendChildNode(temp);
}
return n;
}
Related
I am trying to read correctly a WAVE file, PCM, mono, 16 bits (2 bytes per sample). I have managed to read the header. The problem is reading (writing) the data part.
As far as I understand the 16-bit samples in the data chunk are little-endian, and "split" into two chunks of 8 bits each. So for me a way to read the correct data should be:
Read file and put chunks into two differentint8_t variables (or a std::vector<int8_t>..)
In some way "join" these two variables to make a int16_t and being able to process it.
The problem is I have no idea on how to deal with the little-endianness and the fact that these samples aren't unsigned, so I can't use the << operator.
This is one of the test I've done, without success:
int8_t buffer[], firstbyte,secondbyte;
int16_t result;
std::vector<int16_t> data;
while(Read bytes and put them in buffer){
for (int j=0;j<bytesReadFromTheFile;j+=2){
firstbyte = buffer[j];
secondbyte = buffer[j+1];
result = (firstbyte);
result = (result << 8)+secondbyte; //shift first byte and add second
data.push_back(result);
}
}
To be more verbose, I am using this code found online and created a class starting from it (The process is the same, but the Class configuration is very long and has many features that aren't useful for this problem):
#include <iostream>
#include <string>
#include <fstream>
#include <cstdint>
using std::cin;
using std::cout;
using std::endl;
using std::fstream;
using std::string;
typedef struct WAV_HEADER
{
/* RIFF Chunk Descriptor */
uint8_t RIFF[4]; // RIFF Header Magic header
uint32_t ChunkSize; // RIFF Chunk Size
uint8_t WAVE[4]; // WAVE Header
/* "fmt" sub-chunk */
uint8_t fmt[4]; // FMT header
uint32_t Subchunk1Size; // Size of the fmt chunk
uint16_t AudioFormat; // Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
uint16_t NumOfChan; // Number of channels 1=Mono 2=Sterio
uint32_t SamplesPerSec; // Sampling Frequency in Hz
uint32_t bytesPerSec; // bytes per second
uint16_t blockAlign; // 2=16-bit mono, 4=16-bit stereo
uint16_t bitsPerSample; // Number of bits per sample
/* "data" sub-chunk */
uint8_t Subchunk2ID[4]; // "data" string
uint32_t Subchunk2Size; // Sampled data length
} wav_hdr;
// Function prototypes
int getFileSize(FILE* inFile);
int main(int argc, char* argv[])
{
wav_hdr wavHeader;
int headerSize = sizeof(wav_hdr), filelength = 0;
const char* filePath;
string input;
if (argc <= 1)
{
cout << "Input wave file name: ";
cin >> input;
cin.get();
filePath = input.c_str();
}
else
{
filePath = argv[1];
cout << "Input wave file name: " << filePath << endl;
}
FILE* wavFile = fopen(filePath, "r");
if (wavFile == nullptr)
{
fprintf(stderr, "Unable to open wave file: %s\n", filePath);
return 1;
}
//Read the header
size_t bytesRead = fread(&wavHeader, 1, headerSize, wavFile);
cout << "Header Read " << bytesRead << " bytes." << endl;
if (bytesRead > 0)
{
//Read the data
uint16_t bytesPerSample = wavHeader.bitsPerSample / 8; //Number of bytes per sample
uint64_t numSamples = wavHeader.ChunkSize / bytesPerSample; //How many samples are in the wav file?
static const uint16_t BUFFER_SIZE = 4096;
int8_t* buffer = new int8_t[BUFFER_SIZE];
while ((bytesRead = fread(buffer, sizeof buffer[0], BUFFER_SIZE / (sizeof buffer[0]), wavFile)) > 0)
{
* /** DO SOMETHING WITH THE WAVE DATA HERE **/ *
cout << "Read " << bytesRead << " bytes." << endl;
}
delete [] buffer;
buffer = nullptr;
filelength = getFileSize(wavFile);
cout << "File is :" << filelength << " bytes." << endl;
cout << "RIFF header :" << wavHeader.RIFF[0] << wavHeader.RIFF[1] << wavHeader.RIFF[2] << wavHeader.RIFF[3] << endl;
cout << "WAVE header :" << wavHeader.WAVE[0] << wavHeader.WAVE[1] << wavHeader.WAVE[2] << wavHeader.WAVE[3] << endl;
cout << "FMT :" << wavHeader.fmt[0] << wavHeader.fmt[1] << wavHeader.fmt[2] << wavHeader.fmt[3] << endl;
cout << "Data size :" << wavHeader.ChunkSize << endl;
// Display the sampling Rate from the header
cout << "Sampling Rate :" << wavHeader.SamplesPerSec << endl;
cout << "Number of bits used :" << wavHeader.bitsPerSample << endl;
cout << "Number of channels :" << wavHeader.NumOfChan << endl;
cout << "Number of bytes per second :" << wavHeader.bytesPerSec << endl;
cout << "Data length :" << wavHeader.Subchunk2Size << endl;
cout << "Audio Format :" << wavHeader.AudioFormat << endl;
// Audio format 1=PCM,6=mulaw,7=alaw, 257=IBM Mu-Law, 258=IBM A-Law, 259=ADPCM
cout << "Block align :" << wavHeader.blockAlign << endl;
cout << "Data string :" << wavHeader.Subchunk2ID[0] << wavHeader.Subchunk2ID[1] << wavHeader.Subchunk2ID[2] << wavHeader.Subchunk2ID[3] << endl;
}
fclose(wavFile);
return 0;
}
// find the file size
int getFileSize(FILE* inFile)
{
int fileSize = 0;
fseek(inFile, 0, SEEK_END);
fileSize = ftell(inFile);
fseek(inFile, 0, SEEK_SET);
return fileSize;
}
The problem is in the /** DO SOMETHING WITH THE WAVE DATA HERE **/ . I have no Idea on how to get the sample value.
I'm a Java programmer, not C++, but I've dealt with this often.
The PCM data is organized by frame. If it's mono, little-endian, 16-bit the first byte will be the lower half of the value, and the second byte will be the upper and include the sign bit. Big-endian will reverse the bytes. If it's stereo, a full frame (I think it's left then right but I'm not sure) is presented intact before moving on to the next frame.
I'm kind of amazed at all the code being shown. In Java, the following suffices for PCM encoded as signed values:
public short[] fromBufferToPCM(short[] audioPCM, byte[] buffer)
{
for (int i = 0, n = buffer.length; i < n; i += 2)
{
audioPCM[i] = (buffer[i] & 0xff) | (buffer[i + 1] << 8);
}
return audioBytes;
}
IDK how to translate that directly to C++, but we are simply OR-ing together the two bytes with the second one first being shifted 8 places to the left. The pure shift picks up the sign bit. (I can't recall why the & 0xff was included--I wrote this a long while back and it works.)
Curious why so many answers are in the comments and not posted as answers. I thought comments were for requests to clarify the OP's question.
something like this works:
int8_t * tempBuffer = new int8_t [numSamples];
int index_for_loop = 0;
float INT16_FAC = pow(2,15) - 1;
double * outbuffer = new double [numSamples];
inside while loop:
for(int i = 0; i < BUFFER_SIZE; i += 2)
{
firstbyte = buffer[i];
secondbyte = buffer[i + 1];
result = firstbyte;
result = (result << 8) +secondbyte;
tempBuffer[index_for_loop] = result;
index_for_loop += 1;
}
then normalize between -1 and 1 by doing:
for(int i = 0; i <numSamples; i ++)
{
outbuffer[i] = float(tempBuffer[i]) / INT16_FAC;
}
got normalize from:
sms-tools
Note : this works for mono files with 44100 samplerate and 16 bit resolution.
I am trying to read a large amount of data (~TB) from two solid state drives (SSDs) (ideally unformatted) and combine the data by a certain algorithm and write it back to another SSD. Initially I implemented a program that reads from two FORMATTED hard drives (OS sees it) and writes to another formatted hard drive. I was able to get about 450 MB/Sec speed (for the whole program). I was using fread() to read data from SSDs.
Then I implemented it with reading from two UNFORMATTED SSDs and writing to a formatted SSD and I am only getting about 200 MB/sec. I am still using fread() and I am puzzled why the speed is slower. To read unformatted hard drives I have to run in the Admin mode and use HANDLE and then cast it to a file pointer by the time I read bytes. So, once the correct hard drives are found the code is the same and I am using freads(). The code is listed below for a test program I used to measure the speed of the formatted vs unformatted reads. In this program I am getting about 200 MB/sec for UNFORMATTED hard drive reads and 500 MB/sec for FORMATTED hard drive reads.
Formatted read program.
while (tempCounter < frames_to_process) //
{
if (fread(frame_header_A, 1, 212052, pFile_A) != 212052) {
printf("Couldn't read 212052 Bytes\n");
if (feof(pFile_A))
{
printf("End of File reached while reading the frame\n");
cout << "Num iterations " << tempCounter << endl;
}
if (ferror(pFile_A))
{
printf("Error reading file while reading for the frame\n");
}
time2 = chrono::high_resolution_clock::now();
time_span = chrono::duration_cast<chrono::duration<double>>(time2 - time1);
cout << "time to create imagery files " << setprecision(10) << time_span.count() << " seconds" << endl << endl;
exit(0);
return 0;
}
tempCounter++;
//let's Check whether the variable end with 0xBCB55757
container_count_A = 1;
container_count_B = 2;
if ((frame_header_A[212048] == (char)0xBC) && (frame_header_A[212049] == (char)0xB5) && (frame_header_A[212050] == (char)0x57) && (frame_header_A[212051] == (char)0x57))
{
unsigned int byte_1 = frame_header_A[27];
unsigned int byte_2 = frame_header_A[26];
unsigned int byte_3 = frame_header_A[25];
unsigned int byte_4 = frame_header_A[24];
}
}
Unformatted reads program. Essentially, it iterates though all connected hard drives and find the specific hard drive A and B using a certain pattern. After that the HANDLE is casted to a regular file pointer and then reuses the code above (formatted case)
FILE *pFile_A = NULL;
FILE *pFile_B = NULL;
FILE* f = NULL;
ofstream writeBinary_Combine;
writeBinary_Combine.open(argv[2], ofstream::out | ofstream::binary);
//
LPCTSTR dsksrc = L"\\\\.\\PhysicalDrive";
wchar_t dsk[512] = L"";
int fd;
bool channel_A_found = false;
bool channel_B_found = false;
char frame_header_A[212052]; //seems I can't have another
char frame_header_B[212052];
unsigned int container_count_A = 1;
unsigned int container_count_B = 2;
char* frame_header_test = new char[212052]; // for some reason I am running out of static memory; so I am using heap to alleviate it ???
HANDLE hDisk;
for (int i = 0; i < 8; i++)
{
swprintf(dsk, 511, L"%s%d", dsksrc, i);
hDisk = CreateFile(dsk, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if (hDisk == INVALID_HANDLE_VALUE)
{
printf("%s%d%s", "couldn't open the drive ", i, "\n");
CloseHandle(hDisk);
}
else
{
printf("%s%d%s", "successfully open the drive ", i, "\n");
fd = _open_osfhandle((intptr_t)hDisk, _O_RDONLY);
char buff_test[500000];
if (fd != -1)
{
f = _fdopen(fd, "rb");
int ret_code = 0;
char drive_header[512];
fread(drive_header, 1, 512, f);
if ((drive_header[510] == (char)0x55) && (drive_header[511] == (char)0xAA)) // test for a formatted drive; is there other identifiers?
{
//bootable drive; not an unformatted drive no need to keep on searching
//_close(fd);
}
else
{
fseek(f, 0, SEEK_SET);
ret_code = find_BCB55757(f, i);
if (ret_code != -1)
{
fseek(f, 0, SEEK_SET);
fread(buff_test, 1, ret_code, f);
fread(frame_header_test, 1, 212052, f);
unsigned int readout_B = 1;
unsigned int cmdout_B = 2;
unsigned int byte_1 = frame_header_test[115];
unsigned int byte_2 = frame_header_test[114];
unsigned int byte_3 = frame_header_test[113];
unsigned int byte_4 = frame_header_test[112];
readout_B = ((byte_4 << 24) & 0xff000000) | ((byte_3 << 16) & 0x00ff0000) | ((byte_2 << 8) & 0x0000ff00) | (byte_1 & 0x000000ff);
byte_1 = frame_header_test[123];
byte_2 = frame_header_test[122];
byte_3 = frame_header_test[121];
byte_4 = frame_header_test[120];
cmdout_B = ((byte_4 << 24) & 0xff000000) | ((byte_3 << 16) & 0x00ff0000) | ((byte_2 << 8) & 0x0000ff00) | (byte_1 & 0x000000ff);
//cout << readout_B << " " << cmdout_B << endl;
size_t result_1 = 0;
if (readout_B == (cmdout_B - 1))
{
channel_B_found = true;
fseek(f, 0, SEEK_SET); //this is the only way to do it
result_1 = fread(buff_test, 1, ret_code, f);
if (result_1 != (size_t)ret_code)
{
cout << "reading error 1 " << endl;
cout << "read bytes " << result_1 << endl;
}
result_1 = fread(frame_header_B, 1, 212052, f);
if (result_1 != (size_t)212052)
{
cout << "reading error 2" << endl;
cout << "read bytes " << result_1 << endl;
}
pFile_B = f;
byte_1 = frame_header_B[27];
byte_2 = frame_header_B[26];
byte_3 = frame_header_B[25];
byte_4 = frame_header_B[24];
cout << "found B" << endl;
}
else
{
channel_A_found = true;
fseek(f, 0, SEEK_SET); //this is the only way to do it
result_1 = fread(buff_test, 1, ret_code, f);
if (result_1 != (size_t)ret_code)
{
cout << "reading error 5" << endl;
cout << "read bytes " << result_1 << endl;
}
result_1 = fread(frame_header_A, 1, 212052, f);
if (result_1 != (size_t)212052)
{
cout << "reading error 6" << endl;
cout << "read bytes " << result_1 << endl;
}
pFile_A = f;
byte_1 = frame_header_A[27];
byte_2 = frame_header_A[26];
byte_3 = frame_header_A[25];
byte_4 = frame_header_A[24];
cout << "found A" << endl;
}
}
//fclose(f);
}
}
else
{
_close(fd);
}
}
if (channel_A_found && channel_B_found)
{
cout << "both drives found" << endl;
break;
}
}// then reuse the code from the formatted case
Here are my questions.
1) why is the speed in the unformatted case lot slower although I am using the same fread(). Does it have to do with setting up reading physical drives the HANDLE
2) Is there a way to improve the speed of the unformatted hard drives? should I be using read() instead of fread() in the unformatted case?
I appreciate any help or ideas!
The following code only saves the first 7 bytes to the file correctly and the remaining 3072-7=3065 bytes are incorrect. "correct" meaning the same value as stored in 'data'.
#define byte unsigned char
void bytesToImage(byte width, byte height, byte* data, size_t byte_count, char* fileNameWithoutExtension)
{
{
std::ofstream file("k3000", std::ios::binary);
file.write((char *)data, 3000);
}
}
However this code does save the first 500 bytes correctly:
#define byte unsigned char
void bytesToImage(byte width, byte height, byte* data, size_t byte_count, char* fileNameWithoutExtension)
{
{
std::ofstream file("k500", std::ios::binary);
file.write((char *)data, 500);
}
}
data has a length of 3072 and the function is called as follows:
size_t imageByteCount = 32 * 32 * 3;
byte* imageBufferOut = (byte*)malloc(sizeof(byte) * imageByteCount);
//(imageBufferOut is initialized...)
bytesToImage(32, 32, imageBufferOut, imageByteCount, "img");
Please excuse the redundant parameters, I have removed as much as possible to try find the bug.
Hex dumps:
Try adding some more instrumentation. For example:
{
cout << "before:";
for (int i = 0; i < 16; ++i)
cout << ' ' << std::hex << int(data[i]);
cout << '\n';
std::ofstream file("k3000", std::ios::binary);
if (file)
cout << "opened\n";
else
cout << "couldn't open\n";
file.write((char *)data, 3000);
file.flush();
if (file)
cout << "wrote ok\n";
else
cout << "write failed\n";
cout << "after:";
for (int i = 0; i < 16; ++i)
cout << ' ' << std::hex << int(data[i]);
cout << '\n';
}
I have a G711 codec, and i would like to convert a wav file and save this. I read a sample.wav file, and convert it with the G711 ITU-T codec, and save the encoded data to an output.wav file.
Here is my code:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "G711.h"
#include <vector>
using namespace std;
// WAVE PCM soundfile format (you can find more in https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ )
typedef struct header_file
{
char chunk_id[4];
int chunk_size;
char format[4];
char subchunk1_id[4];
int subchunk1_size;
short int audio_format;
short int num_channels;
int sample_rate; // sample_rate denotes the sampling rate.
int byte_rate;
short int block_align;
short int bits_per_sample;
char subchunk2_id[4];
int subchunk2_size; // subchunk2_size denotes the number of samples.
} header;
typedef struct header_file* header_p;
int main()
{
FILE * infile = fopen("sample.wav","rb"); // Open wave file in read mode
FILE * outfile = fopen("Output.wav","wb"); // Create output ( wave format) file in write mode
int BUFSIZE = 512; // BUFSIZE can be changed according to the frame size required (eg:512)
int count = 0; // For counting number of frames in wave file.
short int buff16[BUFSIZE]; // short int used for 16 bit as input data format is 16 bit PCM audio
header_p meta = (header_p)malloc(sizeof(header)); // header_p points to a header struct that contains the wave file metadata fields
int nb; // variable storing number of byes returned
if (infile)
{
fread(meta, 1, sizeof(header), infile);
fwrite(meta,1, sizeof(*meta), outfile);
cout << " Size of Header file is "<<sizeof(*meta)<<" bytes" << endl;
cout << " Sampling rate of the input wave file is "<< meta->sample_rate <<" Hz" << endl;
cout << " Number of samples in wave file are " << meta->subchunk2_size << " samples" << endl;
cout << " ID: " << meta->chunk_id << endl;
cout << "chunk_size" << meta->chunk_size << endl;
cout << "format" << meta->format << endl;
cout << "subchunk1"<<meta->subchunk1_id << endl;
cout << "subchunk1_size"<<meta->subchunk1_size << endl;
cout << "audio_format"<<meta->audio_format<<endl;
cout << "num_channels"<<meta->num_channels<<endl;
cout << "byte_rate"<<meta->byte_rate<<endl;
cout << "block_align"<<meta->block_align<<endl;
cout << "bits_per_sample"<<meta->bits_per_sample<<endl;
cout << "subchunk2_id"<<meta->subchunk2_id<<endl;
while (!feof(infile))
{
nb = fread(buff16,1,BUFSIZE,infile); // Reading data in chunks of BUFSIZE
size_t ds = sizeof(buff16) >> 1;
cout << "ds: " << ds <<endl;
vector<short> vec_src(sizeof(buff16), 0);
vector<unsigned char> vec_dst(ds, 0);
for (int i = 0; i < sizeof(buff16); i++) {
vec_src[i] = buff16[i];
}
short* src = &vec_src[0];
uint8_t* dst = &vec_dst[0];
G711::ALawEncode(dst, buff16, sizeof(buff16));
unsigned char arr[ds];
for (unsigned i = 0; i < ds; i++) {
arr[i] = *(dst + i);
}
count++; // Incrementing Number of frames
/* Insert your processing code here*/
fwrite(arr,sizeof(unsigned char),sizeof(arr),outfile); // Writing read data into output file
}
cout << " Number of frames in the input wave file are " <<count << endl;
}
getchar();
return 0;
}
I dont know very well C++, so please help me. If i run this code i get Output.wav file but i cant play. Whats wrong?
I'm trying to read in the Header information of a .wav file.
If I have a .wav file that has a low sample rate (22050) it will read all the information in perfectly, however, if I have a higher Sample Rate (8000) then it fails to read in some information:
"dataSize" set's when using a 22050 .wav file however, when using a 8000 .wav file it does not get set and just displays some random numbers.. e.g. "1672494080" when the actual size is around 4k-4.5k in size.
Any suggestions to where I am going wrong?
EDIT:
#include <iostream>
#include <fstream>
#include <vector>
#include <inttypes.h>
#include <stdint.h>
#include <math.h>
using namespace std;
struct riff_hdr
{
char id[4];
uint32_t size;
char type[4];
};
struct chunk_hdr
{
char id[4];
uint32_t size;
};
struct wavefmt
{
uint16_t format_tag;
uint16_t channels;
uint32_t sample_rate;
uint32_t avg_bytes_sec;
uint16_t block_align;
uint16_t bits_per_sample;
uint16_t extra_size;
};
riff_hdr riff;
chunk_hdr chunk;
wavefmt fmt = {0};
uint32_t padded_size;
vector<uint8_t> chunk_data;
bool readHeader(ifstream &file) {
file.read(reinterpret_cast<char*>(&riff), sizeof(riff));
if (memcmp(riff.id, "RIFF", 4) == 0)
{
cout << "size=" << riff.size << endl;
cout << "id=" << string(riff.type, 4) << endl;
if (memcmp(riff.type, "WAVE", 4) == 0)
{
// chunks can be in any order!
// there is no guarantee that "fmt" is the first chunk.
// there is no guarantee that "fmt" is immediately followed by "data".
// There can be other chunks present!
do {
file.read(reinterpret_cast<char*>(&chunk), sizeof(chunk));
padded_size = ((chunk.size + 2 - 1) & ~1);
cout << "id=" << string(chunk.id, 4) << endl;
cout << "size=" << chunk.size << endl;
cout << "padded size=" << padded_size << endl;
if (memcmp(chunk.id, "fmt\0", 4) == 0)
{
if (chunk.size < sizeof(wavefmt))
{
// error!
file.ignore(padded_size);
}else{
// THIS block doesn't seem to be executing
chunk_data.resize(padded_size);
file.read(reinterpret_cast<char*>(&chunk_data[0]), padded_size);
fmt = *(reinterpret_cast<wavefmt*>(&chunk_data[0]));
cout << "format_tag=" << fmt.format_tag << endl;
cout << "channels=" << fmt.channels << endl;
cout << "sample_rate=" << fmt.sample_rate << endl;
cout << "avg_bytes_sec=" << fmt.avg_bytes_sec << endl;
cout << "block_align=" << fmt.block_align << endl;
cout << "bits_per_sample=" << fmt.bits_per_sample << endl;
cout << "extra_size=" << fmt.extra_size << endl;
}
if(fmt.format_tag != 1)
{
uint8_t *extra_data = &chunk_data[sizeof(wavefmt)];
}
}else if(memcmp(chunk.id, "data", 4) == 0) {
file.ignore(padded_size);
}else{
file.ignore(padded_size);
}
}while ((!file) && (!file.eof()));
}
}
return true;
}
int main()
{
ifstream file("example2.wav");
readHeader(file);
return 0;
}
OUTPUT:
size=41398
id=WAVE
id=fmt
size=18
padded size=18
chunk_data size=0
Where am I going wrong?
You have two problems with your code:
There is a 2-byte integer after the bitsPerSample value that you are not reading. It specifies the size of any extra data in that chunk. If the value of format2 indicates a PCM format only, you can ignore the value of the integer (it will usually be 0 anyway, but it may also be garbage), but you still have to account for its presense. The integer cannot be ignored for non-PCM formats, you have to read the value and then read how many bytes it says. You need to make sure you are reading the entire chunk before then entering your while loop, otherwise you will not be on the correct starting position in the file to read further chunks.
You are not taking into account that chunks are padded to the nearest WORD boundary, but the chunk size does not include any padding. When you call seekg(), you need to round the value up to the next WORD boundary.
Update: based on the new code you posted, it should look more like this instead:
#include <iostream>
#include <fstream>
#include <vector>
#include <inttypes.h>
#include <stdint.h>
#include <math.h>
using namespace std;
// if your compiler does not have pshpack1.h and poppack.h, then
// use #pragma pack instead. It is important that these structures
// be byte-alignd!
#include <pshpack1.h>
struct s_riff_hdr
{
char id[4];
uint32_t size;
char type[4];
};
struct s_chunk_hdr
{
char id[4];
uint32_t size;
};
struct s_wavefmt
{
uint16_t format_tag;
uint16_t channels;
uint32_t sample_rate;
uint32_t avg_bytes_sec;
uint16_t block_align;
};
struct s_wavefmtex
{
s_wavefmt fmt;
uint16_t bits_per_sample;
uint16_t extra_size;
};
struct s_pcmwavefmt
{
s_wavefmt fmt;
uint16_t bits_per_sample;
};
#include <poppack.h>
bool readWave(ifstream &file)
{
s_riff_hdr riff_hdr;
s_chunk_hdr chunk_hdr;
uint32_t padded_size;
vector<uint8_t> fmt_data;
s_wavefmt *fmt = NULL;
file.read(reinterpret_cast<char*>(&riff_hdr), sizeof(riff_hdr));
if (!file) return false;
if (memcmp(riff_hdr.id, "RIFF", 4) != 0) return false;
cout << "size=" << riff_hdr.size << endl;
cout << "type=" << string(riff_hdr.type, 4) << endl;
if (memcmp(riff_hdr.type, "WAVE", 4) != 0) return false;
// chunks can be in any order!
// there is no guarantee that "fmt" is the first chunk.
// there is no guarantee that "fmt" is immediately followed by "data".
// There can be other chunks present!
do
{
file.read(reinterpret_cast<char*>(&chunk_hdr), sizeof(chunk_hdr));
if (!file) return false;
padded_size = ((chunk_hdr.size + 1) & ~1);
cout << "id=" << string(chunk_hdr.id, 4) << endl;
cout << "size=" << chunk_hdr.size << endl;
cout << "padded size=" << padded_size << endl;
if (memcmp(chunk_hdr.id, "fmt ", 4) == 0)
{
if (chunk_hdr.size < sizeof(s_wavefmt)) return false;
fmt_data.resize(padded_size);
file.read(reinterpret_cast<char*>(&fmt_data[0]), padded_size);
if (!file) return false;
fmt = reinterpret_cast<s_wavefmt*>(&fmt_data[0]);
cout << "format_tag=" << fmt->format_tag << endl;
cout << "channels=" << fmt->channels << endl;
cout << "sample_rate=" << fmt->sample_rate << endl;
cout << "avg_bytes_sec=" << fmt->avg_bytes_sec << endl;
cout << "block_align=" << fmt->block_align << endl;
if (fmt->format_tag == 1) // PCM
{
if (chunk_hdr.size < sizeof(s_pcmwavefmt)) return false;
s_pcmwavefmt *pcm_fmt = reinterpret_cast<s_pcmwavefmt*>(fmt);
cout << "bits_per_sample=" << pcm_fmt->bits_per_sample << endl;
}
else
{
if (chunk_hdr.size < sizeof(s_wavefmtex)) return false;
s_wavefmtex *fmt_ex = reinterpret_cast<s_wavefmtex*>(fmt);
cout << "bits_per_sample=" << fmt_ex->bits_per_sample << endl;
cout << "extra_size=" << fmt_ex->extra_size << endl;
if (fmt_ex->extra_size != 0)
{
if (chunk_hdr.size < (sizeof(s_wavefmtex) + fmt_ex->extra_size)) return false;
uint8_t *extra_data = reinterpret_cast<uint8_t*>(fmt_ex + 1);
// use extra_data, up to extra_size bytes, as needed...
}
}
}
else if (memcmp(chunk_hdr.id, "data", 4) == 0)
{
// process chunk data, according to fmt, as needed...
file.ignore(padded_size);
if (!file) return false;
}
else
{
// process other chunks as needed...
file.ignore(padded_size);
if (!file) return false;
}
}
while (!file.eof());
return true;
}
int main()
{
ifstream file("example2.wav");
readWave(file);
return 0;
}