Skip to content

Instantly share code, notes, and snippets.

@Foaly
Last active November 10, 2015 14:54
Show Gist options
  • Save Foaly/31c2176b439ffc4bb6fc to your computer and use it in GitHub Desktop.
Save Foaly/31c2176b439ffc4bb6fc to your computer and use it in GitHub Desktop.
#define _USE_MATH_DEFINES
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <iostream>
#include <iomanip>
#include <vector>
#include <cmath>
#include <iterator>
#include <algorithm>
float toGrayscale(const sf::Color& color);
void addSineWave(std::vector<float>& samples, float amplitude, float frequency, int sampleRate);
int main() {
sf::Clock processingClock;
// load the image
sf::Image image;
if (!image.loadFromFile("data/eye.png"))
{
std::cerr << "Error loading image" << std::endl;
return -1;
}
auto imageSize = image.getSize();
const int maxFrequency = 20000;
const int minFrequency = 200;
const int samplingRate = 44100;
const int pxPerSec = 30;
std::cout << "Frequency range: " << minFrequency << " - " << maxFrequency << std::endl;
std::cout << "Pixel per second: " << pxPerSec << std::endl;
std::cout << "Samplingrate: " << samplingRate << std::endl;
const int freqencyRange = maxFrequency - minFrequency;
const int interval = freqencyRange / imageSize.y; // TODO: what is this? frequency band (height) per pixel?
const unsigned int numberOfSamplesPerPixel = samplingRate / pxPerSec;
std::vector<sf::Int16> samples;
std::vector<float> samplePerPixel(numberOfSamplesPerPixel, 0.f);
for (unsigned int x = 0; x < imageSize.x; ++x)
{
// reset the vector with 0
std::fill(samplePerPixel.begin(), samplePerPixel.end(), 0.f);
for (unsigned int y = 0; y < imageSize.y; ++y)
{
const float grayValue = toGrayscale(image.getPixel(x, y)) / 255.f;
if (grayValue > 0.f)
{
const int yInverted = (imageSize.y - 1) - y;
const float frequency = yInverted * interval + minFrequency;
addSineWave(samplePerPixel, grayValue, frequency, samplingRate);
}
}
// find the absolute maximum
auto minmaxElement = std::minmax_element(samplePerPixel.begin(), samplePerPixel.end());
float absMax = std::max(std::abs(*(minmaxElement.first)), *(minmaxElement.second));
// normalize vector
std::transform(samplePerPixel.begin(), samplePerPixel.end(), samplePerPixel.begin(),
std::bind2nd(std::divides<float>(), absMax));
//std::transform(samplePerPixel.begin(), samplePerPixel.end(), samplePerPixel.begin(),
// [&absMax](auto x) { return x * absMax } );
// scale the float values (-1, 1) to signed int values (-32768, 32767)
std::transform(samplePerPixel.begin(), samplePerPixel.end(), samplePerPixel.begin(),
std::bind2nd(std::multiplies<float>(), 32767));
// append to the buffers samples
std::copy(samplePerPixel.begin(), samplePerPixel.end(), back_inserter(samples));
// progress counter
std::cout << std::setprecision(2) << "Generating sound: " << static_cast<float>(x) / imageSize.x * 100.f << "% \r";
std::cout.flush();
}
std::cout << "Generating sound complete" << std::endl;
std::cout << "Calculation time: " << processingClock.getElapsedTime().asSeconds() << " seconds" << std::endl;
sf::SoundBuffer soundBuffer;
if (!soundBuffer.loadFromSamples(&samples[0], samples.size(), 1, samplingRate)) {
std::cerr << "Loading sound from buffer failed!" << std::endl;
return -1;
}
// play the generated sound until it's over or escape was pressed
sf::Sound sound(soundBuffer);
sound.play();
while (sound.getStatus() == sf::Sound::Playing)
{
sf::sleep(sf::milliseconds(100));
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
break;
}
// save sound to file
if (!soundBuffer.saveToFile("out.wav"))
{
std::cerr << "Saving sound buffer to file failed!" << std::endl;
return -1;
}
return 0;
}
/**
* \brief
*
* \param samples The sample vector into which the sounds are added
* \param amplitude A float value between 0.f and 1.f
* \param frequency The frequency the tone should have
* \param sampleRate The sampling rate of the tone
*/
void addSineWave(std::vector<float>& samples, float amplitude, float frequency, int sampleRate)
{
const float twoPi = 2.f * static_cast<float>(M_PI);
const float phase = 0.f;
for (int i = 0; i < samples.size(); ++i)
{
samples[i] += std::sin(twoPi * frequency / sampleRate * i + phase) * amplitude;
}
}
/**
* @brief This function converts a sf::Color to a grayscale value using the NTSC
* conversion weights. (see https://en.wikipedia.org/wiki/Grayscale#Luma_coding_in_video_systems)
*
* @param color The color to convert
* @return The grayscale value of the given color in range [0.f, 255.f]
*/
float toGrayscale(const sf::Color& color)
{
return 0.299f * color.r + 0.587f * color.g + 0.114f * color.b;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment