Vous êtes sur la page 1sur 8

/***************************************************************************

Below is implementation for converting a wav file from stereo to mono. It


takes a wav file with either 16 or 8 bit and converts it to mono with
normalized volume. I enjoyed working on this assignment and learning how to
normalize sound files.
****************************************************************************/
#include
#include
#include
#include
using
using
const
const

<fstream>
<iostream>
<sstream>
"to_mono.h"

std::cout;
std::endl;
int sixteenBitMax = pow(2,15) - 1;
int sixteenBitMin = -pow(2,15);

wavConverter::wavConverter() : gainFactor(0),dcOffset(0),newAmp(0)
{}
/***************************************************************************
Reads in a wave file and puts the read in data into a buffer which will be
used in calculations later on.
****************************************************************************/
void wavConverter::ReadInWavFile(std::string name)
{
//read in the file and check to make sure it opened correctly
std::fstream myfile(name, std::ios_base::in | std::ios_base::binary);
if(!myfile.is_open())
{
cout << "file not opened." << endl;
return;
}
myfile.read(reinterpret_cast<char*>(&wavheader),sizeof(header));
//read in the values for voltage conversion later
readBuffer = new char[wavheader.data_chunk_size];
//read in the data after the header which will be used for the voltage
values
myfile.read(readBuffer,wavheader.data_chunk_size);
//close the file after we have read in the data
myfile.close();
}
/***************************************************************************
Converts the file from stereo to mono. If its in stereo form, it will
properly calculate the correct voltages values by combining the two stereo
channels. Depending if its 16 or 8 bit, the function will properly offset the
values once the two channels are combined into mono form.
****************************************************************************/

void wavConverter::ConvertToMonoAndVoltage(void)
{
//amount we are going to walk when we are putting voltage values into the
container
unsigned int walkAmount = (float)wavheader.data_chunk_size
/(float)wavheader.number_of_channels;
//check for stereo (2 channels)
if(wavheader.number_of_channels == 2)
{
//check for 16 bit
if(wavheader.bits_per_sample == 16)
{
//16 bit uses shorts for the data thats being stored
short* stereo16 = reinterpret_cast<short*>(readBuffer);
//calculate how much we need to walk as we start storing in the data
walkAmount /= sizeof(short);
//loop through all of the voltage values and start storing them
for(int i = 0; i < walkAmount;++i)
{
//since this is stereo, we have 2 channels( the left and right)
float left_channel = (float)stereo16[wavheader.number_of_channels*i];
float right_channel =
(float)stereo16[wavheader.number_of_channels*i+1];
//add the two channel amounts together which will convert the file
into mono
float newSum = left_channel + right_channel;
//put the new calculated voltage value into the container
values.push_back(newSum);
}
//since we have converted to voltage, we need to update the chunksize,
data chunksize, block alignment and number of channels
//if we dont do this, the data will not be outputted properly
wavheader.chunk_size = sizeof(short)*values.size() + 36;
wavheader.data_chunk_size = values.size()*sizeof(short);
wavheader.block_align = (wavheader.number_of_channels*
wavheader.bytes_per_second)/8;
wavheader.number_of_channels = 1;
}
//check if we have an 8 bit sample
else if(wavheader.bits_per_sample == 8)
{
//8 bit samples use characters so we need to convert the buffer to a char
pointer
char* stereo8 = reinterpret_cast<char*>(readBuffer);
for(int i = 0; i < walkAmount;++i)
{
//grab the left and right channel values
float left_channel = (float)stereo8[wavheader.number_of_channels*i];

float right_channel =
(float)stereo8[wavheader.number_of_channels*i+1];
//we need to offset the channels by 128
left_channel -= 128;
right_channel -= 128;
//add the channels together which will convert them to mono
float newSum = left_channel + right_channel;
values.push_back(newSum);
//chunk size = data size + size of header
wavheader.chunk_size = values.size() + 36;
}
//since we have converted to voltage, we need to update the chunksize,
data chunksize, block alignment and number of channels
//if we dont do this, the data will not be outputted properly
wavheader.data_chunk_size = values.size();
wavheader.block_align = (wavheader.number_of_channels*
wavheader.bytes_per_second)/8;
wavheader.number_of_channels = 1;
}
else
{
return;
}
}
//case when its mono
else if(wavheader.number_of_channels == 1)
{
//check if we have 16 bit sample
if(wavheader.bits_per_sample == 16)
{
//16 bit uses shorts for the data thats being stored
short* mono16 = reinterpret_cast<short*>(readBuffer);
//calculate the amount we will be walking through the voltage data
walkAmount /= sizeof(short);
for(int i = 0; i < walkAmount;++i)
{
//here we are in mono so we only have one channel
//put the channel value into the container
float left_channel = (float)mono16[wavheader.number_of_channels*i];
values.push_back(left_channel);
}
}
//check if its an 8 bit sample
else if(wavheader.bits_per_sample == 8)
{
//8 bit uses chars
char* mono8 = reinterpret_cast<char*>(readBuffer);

for(int i = 0; i < walkAmount;++i)


{
//calculate the channel values and offset it by 128 for 8 bit samples
float left_channel = (float)mono8[wavheader.number_of_channels*i];
left_channel -= 128;
//put the calculated value into the container
values.push_back(left_channel);
}
}
else
{
return;
}
}
else
{
return;
}
}
/****************************************************************************
This function properly computes the DC offset which is used in normalizing
the data computed above.
****************************************************************************/
void wavConverter::ComputeDcOffsetAndNewValues(void)
{
//compute the DC offset, the offset is the average of the voltage values in
the container
for(int i = 0; i < values.size();++i)
{
//add all the values in the container together
dcOffset+= values[i];
}
//divide by the number of elements, which will give us the average
dcOffset *= (1.0f/(float)values.size());
//finally take the computed offset and subtract each value from the offset
for(int i = 0; i < values.size();++i)
{
values[i] -= dcOffset;
}
}
/***************************************************************************
Computes the gain factor based on the provided decibel amount from the user.
****************************************************************************/
void wavConverter::ComputeGainFactor(float dBamount)
{
//finds the gain factor gainfactor = 10^((dbAmount from user)/20)
gainFactor = pow(10,(dBamount/20));
}

/***************************************************************************
This function computes the final gain factor and maximum amplitude we will be
using with our audio data.
****************************************************************************/
void wavConverter::ComputeFinalGainAndMaxAmp(void)
{
//unsigned char = 8 bit
short = 16 bit
float maxValue = FLT_MIN;
//we need to find the highest absolute voltage value in our container
for(int i = 0; i < values.size();++i)
{
//set my value equal to the value im currently at and then check if its
greater than my highest value so far
float currentvalue = abs(values[i]);
//if it is, then update my greatest value
if(currentvalue > maxValue)
{
maxValue = currentvalue;
}
}
//if we have an 8 bit sample, we need to offset the amplitude by 8 bit
max(127)
if(wavheader.bits_per_sample == 8)
{
newAmp = gainFactor*127;
}
//otherwise we need to offset it by 16 bit max
else
{
newAmp = gainFactor* (sixteenBitMax);
}
//finally the new gain factor will by the amplitude / max value
gainFactor = newAmp/maxValue;
}

/***************************************************************************
Function calculates the final voltage values we will be sending back to the
buffer to write to the final output wave file
****************************************************************************/
void wavConverter::CalcFinalValues(void)
{
//finally we can calculate the final values which will be written to the
output file
//we need to multiply the values by the gain factor

for(int i = 0; i < values.size();++i)


{
values[i] *= gainFactor;
}
//create a buffer that will be used to write the data
writeBuffer = new char[wavheader.data_chunk_size];
//check if its 8 bit sample
if(wavheader.bits_per_sample == 8)
{
for(int i = 0; i < values.size(); ++i)
{
//add 128 to the values and clamp them in the range from 0 to 255
values[i] += 128;
if(values[i] < 0)
{
values[i] = 0;
}
if(values[i] > 255)
{
values[i] = 255;
}
}
unsigned char* mono8 = reinterpret_cast<unsigned char*>(writeBuffer);
//put the values into the buffer
for(int i = 0; i < values.size(); ++i)
{
mono8[i] = values[i];
}
}
//16 bit -2^15 to 2^15 - 1
else
{
for(int i = 0; i < values.size(); ++i)
{
if(values[i] < sixteenBitMin)
{
values[i] = sixteenBitMin;
}
if(values[i] > sixteenBitMax)
{
values[i] = sixteenBitMax;
}
}
short* mono16 = reinterpret_cast<short*>(writeBuffer);
for(int i = 0; i < values.size(); ++i)

{
mono16[i] = values[i];
}
}
}
/***************************************************************************
Writes the buffer data into the output wave file
****************************************************************************/
void wavConverter::WriteFinalWavFile(void)
{
//output the wave fail and write the new buffer data with the final values
to it
std::fstream out("to_mono.wav", std::ios_base::out |
std::ios_base::binary);
out.write(reinterpret_cast<char*>(&wavheader),sizeof(header));
out.write(writeBuffer,wavheader.data_chunk_size);
}
/***************************************************************************
Function used for printing out header data that is useful for debugging
****************************************************************************/
void wavConverter::PrintResults(void)
{
std::string test;
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout
cout

<<
<<
<<
<<
<<
<<
<<
<<
<<
<<
<<
<<

"chunk size: " << wavheader.chunk_size << endl;


"wave_format: " << wavheader.wave_fmt << endl;
"format_chunk: " << wavheader.fmt_chunk << endl;
"format_chunk_size: " << wavheader.fmt_chunk_size << endl;
"audio format: " << wavheader.audio_format << endl;
"number channels: " << wavheader.number_of_channels << endl;
"sampling rate: " << wavheader.sampling_rate << endl;
"bytes per sec: " << wavheader.bytes_per_second << endl;
"block align: " << wavheader.block_align << endl;
"bits per sample: " << wavheader.bits_per_sample << endl;
"data chunk: " << wavheader.data_chunk << endl;
"data chunk size: " << wavheader.data_chunk_size << endl;

}
/***************************************************************************
Main function that executes the functions above to make the program function
correctly.
****************************************************************************/
int main (int argc, char *argv[])
{
if (argc != 3)
return 0;

//first command line argument is the filename


std::string filename = argv[1];
//second command line argument is the DB amount we want
float decibal_Amount = atof(argv[2]);
std::stringstream ss;
ss << decibal_Amount;
cout << "filename: " <<filename <<endl;
cout << "DB amount: " << decibal_Amount <<endl;
wavConverter converter;
converter.ReadInWavFile(filename);
converter.ConvertToMonoAndVoltage();
converter.ComputeDcOffsetAndNewValues();
converter.ComputeGainFactor(decibal_Amount);
converter.ComputeFinalGainAndMaxAmp();
converter.CalcFinalValues();
converter.WriteFinalWavFile();
}

Vous aimerez peut-être aussi