Test-Driving Changes

We can now test-drive our changes to writeData to incorporate the number of channels. The channel count represents the number of tracks (sound from separate sources) to play simultaneously. For monaural (mono) output, the channel count is one. For stereo, it is two. Playback of the WAV requires iterating through all the samples in order. A given sample is comprised of a series of subsamples, one per channel. Monaural representation for a four-sample audio clip, where a sample is a single byte, might look like this:

 
AA BB CC DD

Suppose there is a second channel with the following sample sequence:

 
01 02 03 04

The resulting WAV stream should appear like this:

 
AA 01 BB 02 CC 03 DD 04

Here’s a test showing how increasing the channel requires more bytes in the data stream:

wav/10/WavReaderTest.cpp
 
#include "CppUTest/TestHarness.h"
 
#include "WavReader.h"
 
#include <iostream>
 
#include <string>
 
#include <sstream>
 
 
using​ ​namespace​ std;
 
 
TEST_GROUP(WavReader) {
 
};
 
 
TEST(WavReader_WriteSamples, IncorporatesChannelCount) {
 
char​ data[] { ​"0123456789ABCDEFG"​ };
 
uint32_t bytesPerSample { 2 };
 
uint32_t startingSample { 0 };
 
uint32_t samplesToWrite { 2 };
 
uint32_t channels { 2 };
 
reader.writeSamples(
 
&out, data, startingSample, samplesToWrite, bytesPerSample, channels);
 
CHECK_EQUAL(​"01234567"​, out.str());
 
}

To avoid having to change a number of other tests right now, we can default the channels parameter. As usual, our goal is to get tests passing and then clean up.

wav/10/WavReader.h
 
void​ writeSamples(std::ostream* out, ​char​* data,
 
uint32_t startingSample,
 
uint32_t samplesToWrite,
 
uint32_t bytesPerSample,
*
uint32_t channels=1);

We now implement the code that makes the new test pass.

wav/10/WavReader.cpp
 
void​ WavReader::writeSamples(ostream* out, ​char​* data,
 
uint32_t startingSample,
 
uint32_t samplesToWrite,
 
uint32_t bytesPerSample,
*
uint32_t channels) {
 
rLog(channel, ​"writing %i samples"​, samplesToWrite);
 
 
for​ (​auto​ sample = startingSample;
 
sample < startingSample + samplesToWrite;
 
sample++) {
*
auto​ byteOffsetForSample = sample * bytesPerSample * channels;
*
*
for​ (uint32_t channel{0}; channel < channels; channel++) {
*
auto​ byteOffsetForChannel =
*
byteOffsetForSample + (channel * bytesPerSample);
 
for​ (uint32_t byte{0}; byte < bytesPerSample; byte++)
*
out->put(data[byteOffsetForChannel + byte]);
*
}
 
}
 
}

There’s one more thing to correct. When we write the new ten-second WAV, we update the data chunk’s length. The length is currently incorrect since it does not factor in the number of channels. It’s a one-liner, but we’ll happily extract it to a function we can test and correct. We follow the same pattern. We write a test to characterize existing behavior, alter the test to specify new behavior, and change the production code. Here are the resulting test, extracted function, and client code in open that calls the new function:

wav/11/WavReaderTest.cpp
 
TEST_GROUP(WavReader_DataLength) {
 
WavReader reader{​""​,​""​};
 
};
 
 
TEST(WavReader_DataLength, IsProductOfChannels_BytesPerSample_and_Samples) {
 
uint32_t bytesPerSample{ 2 };
 
uint32_t samples { 5 };
 
uint32_t channels { 4 };
 
 
uint32_t length { reader.dataLength(bytesPerSample, samples, channels) };
 
 
CHECK_EQUAL(2 * 5 * 4, length);
 
}
wav/11/WavReader.cpp
 
// ...
 
rLog(channel, ​"total seconds %u "​, totalSeconds);
 
*
dataChunk.length = dataLength(
*
samplesToWrite,
*
bytesPerSample,
*
formatSubchunk.channels);
 
 
out.write(​reinterpret_cast​<​char​*>(&dataChunk), ​sizeof​(DataChunk));
 
// ...
 
 
uint32_t WavReader::dataLength(
 
uint32_t samples,
 
uint32_t bytesPerSample,
 
uint32_t channels
 
) ​const​ {
 
return​ samples * bytesPerSample * channels;
 
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.183.10