TEST_F(WaveformGeneratorTest, shouldComputeMaxAndMinValuesFromMonoInput) { WaveformBuffer buffer; const int samples_per_pixel = 300; SamplesPerPixelScaleFactor scale_factor(samples_per_pixel); WaveformGenerator generator(buffer, scale_factor); const int sample_rate = 44100; const int channels = 1; const int BUFFER_SIZE = 512; short samples[BUFFER_SIZE]; memset(samples, 0, sizeof(samples)); const int frames = BUFFER_SIZE / channels; bool result = generator.init(sample_rate, channels, BUFFER_SIZE); ASSERT_TRUE(result); ASSERT_TRUE(error.str().empty()); // samples for first waveform data point samples[0] = 100; samples[100] = 98; samples[200] = -98; samples[299] = -102; // samples for second waveform data point samples[300] = 197; samples[400] = -200; samples[450] = -197; samples[511] = 202; result = generator.process(samples, frames); ASSERT_TRUE(result); generator.done(); // Check contents of buffer ASSERT_THAT(buffer.getSampleRate(), Eq(44100)); ASSERT_THAT(buffer.getSamplesPerPixel(), Eq(300)); ASSERT_THAT(buffer.getSize(), Eq(2)); // 512 / 300 = 1 remainder 212 // => 2 output points total // Check min and max values ASSERT_THAT(buffer.getMinSample(0), Eq(-102)); ASSERT_THAT(buffer.getMaxSample(0), Eq(100)); ASSERT_THAT(buffer.getMinSample(1), Eq(-200)); ASSERT_THAT(buffer.getMaxSample(1), Eq(202)); }
void GdImageRenderer::drawWaveform(const WaveformBuffer& buffer) const { // Avoid drawing over the right border const int max_x = render_axis_labels_ ? image_width_ - 1 : image_width_; // Avoid drawing over the top and bottom borders const int wave_bottom_y = render_axis_labels_ ? image_height_ - 2 : image_height_ - 1; const int max_wave_height = render_axis_labels_ ? image_height_ - 2 : image_height_; const int buffer_size = buffer.getSize(); // Avoid drawing over the left border int x = render_axis_labels_ ? 1 : 0; int i = render_axis_labels_ ? start_index_ + 1 : start_index_; for (; x < max_x && i < buffer_size; ++i, ++x) { // convert range [-32768, 32727] to [0, 65535] int low = buffer.getMinSample(i) + 32768; int high = buffer.getMaxSample(i) + 32768; // scale to fit the bitmap int low_y = wave_bottom_y - low * max_wave_height / 65536; int high_y = wave_bottom_y - high * max_wave_height / 65536; gdImageLine(image_, x, low_y, x, high_y, waveform_color_); } }
static std::pair<int, int> getAmplitudeRange( const WaveformBuffer& buffer, int start_index, int end_index) { int low = std::numeric_limits<int>::max(); int high = std::numeric_limits<int>::min(); const int channels = buffer.getChannels(); for (int i = start_index; i != end_index; ++i) { for (int channel = 0; channel < channels; ++channel) { const int min = buffer.getMinSample(channel, i); const int max = buffer.getMaxSample(channel, i); if (min < low) { low = min; } if (max > high) { high = max; } } } return std::make_pair(low, high); }
void GdImageRenderer::drawWaveform(const WaveformBuffer& buffer) const { const int max_x = image_width_ - 1; const int wave_bottom_y = image_height_ - 2; const int max_wave_height = image_height_ - 3; const int buffer_size = buffer.getSize(); int x = 1; // Avoid drawing over the left border int i = start_index_ + 1; for (; x < max_x && i < buffer_size; ++i, ++x) { // convert range [-32768, 32727] to [0, 65535] int low = buffer.getMinSample(i) + 32768; int high = buffer.getMaxSample(i) + 32768; // scale to fit the bitmap int low_y = wave_bottom_y - low * max_wave_height / 65536; int high_y = wave_bottom_y - high * max_wave_height / 65536; gdImageLine(image_, x, low_y, x, high_y, wave_color_); } }
void GdImageRenderer::drawWaveform(const WaveformBuffer& buffer) const { // Avoid drawing over the right border const int max_x = render_axis_labels_ ? image_width_ - 1 : image_width_; // Avoid drawing over the top and bottom borders const int top_y = render_axis_labels_ ? 1 : 0; const int bottom_y = render_axis_labels_ ? image_height_ - 2 : image_height_ - 1; const int buffer_size = buffer.getSize(); // Avoid drawing over the left border const int start_x = render_axis_labels_ ? 1 : 0; const int start_index = render_axis_labels_ ? start_index_ + 1 : start_index_; double amplitude_scale; if (auto_amplitude_scale_) { int end_index = start_index + max_x; if (end_index > buffer_size) { end_index = buffer_size; } std::pair<int, int> range = getAmplitudeRange(buffer, start_index, end_index); double amplitude_scale_high = (range.second == 0) ? 1.0 : 32767.0 / range.second; double amplitude_scale_low = (range.first == 0) ? 1.0 : 32767.0 / range.first; amplitude_scale = std::fabs(std::min(amplitude_scale_high, amplitude_scale_low)); } else { amplitude_scale = amplitude_scale_; } output_stream << "Amplitude scale: " << amplitude_scale << '\n'; const int channels = buffer.getChannels(); int available_height = bottom_y - top_y + 1; const int row_height = available_height / channels; int waveform_top_y = render_axis_labels_ ? 1 : 0; for (int channel = 0; channel < channels; ++channel) { int waveform_bottom_y; if (channel == channels - 1) { waveform_bottom_y = waveform_top_y + available_height - 1; } else { waveform_bottom_y = waveform_top_y + row_height; } const int height = waveform_bottom_y - waveform_top_y + 1; for (int i = start_index, x = start_x; x < max_x && i < buffer_size; ++i, ++x) { // Convert range [-32768, 32727] to [0, 65535] int low = scale(buffer.getMinSample(channel, i), amplitude_scale) + 32768; int high = scale(buffer.getMaxSample(channel, i), amplitude_scale) + 32768; // Scale to fit the bitmap int high_y = waveform_top_y + height - 1 - high * height / 65536; int low_y = waveform_top_y + height - 1 - low * height / 65536; gdImageLine(image_, x, low_y, x, high_y, waveform_color_); } available_height -= row_height + 1; waveform_top_y += row_height + 1; } }