I'm trying to save the Spectrum in my FMOD_DSP_PARAMETER_FFT but I'm only receiving the spectrum full of zeros, if you can watch my mistake I will agree, I think that I'm not connecting well the DSP to the channel or something similar because I don't find the error in the code.
My code now is like this:
FMOD::System *system;
FMOD::Sound *sound1;
FMOD::Channel *channel = 0;
FMOD::ChannelGroup *mastergroup;
FMOD::ChannelControl *control;
FMOD::DSP *mydsp, *dsphead, *dspchannelmixer;
FMOD::DSPConnection *conection;
FMOD_RESULT result;
unsigned int version;
result = FMOD::System_Create(&system);
result = system->getVersion(&version);
result = system->init(32, FMOD_INIT_NORMAL, NULL);
result = system->createSound("MySong.mp3",FMOD_DEFAULT, 0, &sound1);
result = sound1->setMode(FMOD_LOOP_NORMAL);
result = system->playSound(sound1, 0, true, &channel);
/*
Create the DSP effect.
*/
result = system->getMasterChannelGroup(&mastergroup);
result = system->createDSPByType(FMOD_DSP_TYPE_FFT, &mydsp);
result = system->getMasterChannelGroup(&mastergroup);
result = mastergroup->addDSP(0, mydsp);
result = mydsp->setBypass(true);
result = mydsp->setActive(true);
char s[256];
unsigned int len;
float freq[32];
float fft = 0;
std::vector<float> fftheights;
float m_spectrum_data[FFT_NUM_BINS];
while (1) { //program loop
unsigned int ms = 0;
unsigned int lenms = 0;
bool playing = 0;
bool paused = 0;
int channelsplaying = 0;
if (channel)
{
FMOD::Sound *currentsound = 0;
result = channel->setPaused(false);
result = channel->setMute(false);
result = channel->isPlaying(&playing);
result = channel->getPaused(&paused);
result = channel->setVolume(0.5);
result = channel->getPosition(&ms, FMOD_TIMEUNIT_MS);
channel->getCurrentSound(¤tsound);
if (currentsound)
{
result = currentsound->getLength(&lenms, FMOD_TIMEUNIT_MS);
}
}
system->getChannelsPlaying(&channelsplaying);
FMOD_DSP_PARAMETER_FFT *fftparameter;
float val;
char s[256];
unsigned int len;
float *data = 0;
float freq[32];
int rate, chan, nyquist;
int windowsize = 1024;
result = system->getSoftwareFormat(&rate, 0, 0);
result = mydsp->setParameterInt(FMOD_DSP_FFT_WINDOWTYPE, FMOD_DSP_FFT_WINDOW_TRIANGLE);
result = mydsp->setParameterInt(FMOD_DSP_FFT_WINDOWSIZE, windowsize);
result = mydsp->getParameterFloat(FMOD_DSP_FFT_DOMINANT_FREQ, &val, 0, 0);
result = mydsp->getParameterData(FMOD_DSP_FFT_SPECTRUMDATA, (void **)&fftparameter, &len, s, 256);
nyquist = windowsize / 2;
for (chan = 0; chan < 2; chan++)
{
float average = 0.0f;
float power = 0.0f;
for (int i = 0; i < nyquist - 1; ++i)
{
float hz = i * (rate * 0.5f) / (nyquist - 1);
int index = i + (16384 * chan);
if (fftparameter->spectrum[chan][i] > 0.0001f) // arbitrary cutoff to filter out noise
{
average += data[index] * hz;
power += data[index];
}
}
if (power > 0.001f)
{
freq[chan] = average / power;
}
else
{
freq[chan] = 0;
}
}
printf("\ndom freq = %d : %.02f %.02f\n", (int)val, freq[0], freq[1]);
}
My fftparameter->spectrum is always an array of zero values...
Is posible to connect it without modify the sound that is playing??
Thank you.
There are a few standout issues in your code example.
The FFT DSP has been bypassed with result = mydsp->setBypass(true); causing it to not process.
There are no calls to System::update in the main loop.
The main loop has no sleep so it will spin as fast as possible.
I think your main issue is probably the setBypass call, use setBypass(false).
Related
I am trying to make brown noise in C++, and to play the sound of it. You can hear the brown noise, but I constantly hear clicking in the background and I don't know why.
Here is my code:
#include <xaudio2.h>
#include <iostream>
#include <random>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
XAUDIO2_BUFFER buffer;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
BYTE pDataBuffer[2*l];
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
w[0] = w[l - 1] + dis(gen)*ampl;
for (int t = 1; t < l; t++) {
tt = (float)(t + frame*l); //total time
w[t] = w[t - 1] + dis(gen)*ampl;
if (w[t] > ampl) {
cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16*(intw/pow16[k]));
intw -= bytw[k]*(pow16[k]/16);
bytw[k] += (BYTE)(intw/(pow16[k]/16));
intw -= (intw/(pow16[k]/16))*pow16[k]/16;
}
pDataBuffer[2*t] = bytw[0];
pDataBuffer[2*t + 1] = bytw[1];
}
cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) {}
}
buffer.AudioBytes = 2*l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer;
buffer.Flags = XAUDIO2_END_OF_STREAM;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = {0};
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
cout << "start";
while (loop()) {}
return 0;
}
The line before the for-loop in loop() is to make sure that the first element nicely attaches itself to the last element of the previous iteration.
To make sure that w doesn't go over ampl or under -ampl, I have added a couple lines that make them bounce back, and I make it output "upper edge" or "lower edge" respectively so that you know when this is happening. As you notice, the clicking also happens when the w is not near the edges.
As a test to make sure it isn't because of XAudio2 being implemented wrongly, you can comment the first line in loop() that defines the first element of w; make the for-loop (in the next line) start from 0; comment the lines that create the brown noise; and uncomment one of the two lines after that: the first line to hear a sine wave sound, the second line to hear a square wave sound (both with a frequency of about 44100/1000 = 44.1 Hz, which you can change around by changing how p is initialized in init()). You will (hopefully) hear a clean sine/square wave sound.
So what is going wrong?
You have two issues in your code:
You only have a single buffer therefore its near impossible to submit a new buffer for playing quickly enough after the buffer stops playing for there to not be a gap between buffers. You are also modifying the buffer data whilst it is being played which will corrupt the output. You should use multiple buffers. With enough buffers this would also allow you to add some short sleeps to your while loop which is checking BuffersQueued to reduce the CPU usage.
You never set pDataBuffer[0] or pDataBuffer[1] so they will always be 0.
This code works:
#include <xaudio2.h>
#include <iostream>
#include <random>
#include <array>
#include <thread>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
const size_t bufferCount = 64;
std::array<XAUDIO2_BUFFER, bufferCount> buffers;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
std::array<std::array<BYTE,2 * l>, bufferCount> pDataBuffers;
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
float prevW = w[l - 1];
auto& pDataBuffer = pDataBuffers[frame & (bufferCount-1)];
auto& buffer = buffers[frame & (bufferCount - 1)];
for (int t = 0; t < l; t++) {
tt = (float)(t + frame * l); //total time
w[t] = prevW + dis(gen) * ampl;
if (w[t] > ampl) {
//cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
//cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
prevW = w[t];
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16 * (intw / pow16[k]));
intw -= bytw[k] * (pow16[k] / 16);
bytw[k] += (BYTE)(intw / (pow16[k] / 16));
intw -= (intw / (pow16[k] / 16)) * pow16[k] / 16;
}
pDataBuffer[2 * t] = bytw[0];
pDataBuffer[2 * t + 1] = bytw[1];
}
//cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(1); }
}
buffer.AudioBytes = 2 * l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer.data();
buffer.Flags = 0;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = { 0 };
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
while (loop()) {}
return 0;
}
I haven't tried to follow all of your logic but it seems over complicated and could definitely be simplified.
The massive use of global variables is also not a great way to write a program. You should move variables inside the functions where possible, otherwise either pass them to the function as arguments or use a class to hold the state.
I'm starting with my c++ threads and don't understand some basic stuff. That's Mandelbrot example, it generates fractal image.
It's not my code, I just did some changes (here's original: https://rosettacode.org/wiki/Mandelbrot_set#PPM_non_interactive)
I have this function which generates matrix with colors to save to file:
vector<unsigned char *> drawMandelbrot()
{
/* screen ( integer) coordinate */
int iX, iY;
double Cx, Cy;
const double CxMin = -2.5;
const double CxMax = 1.5;
const double CyMin = -2.0;
const double CyMax = 2.0;
double PixelWidth = (CxMax - CxMin) / iXmax;
double PixelHeight = (CyMax - CyMin) / iYmax;
int Index = 0;
const int IterationMax = 200;
unsigned char color[3];
vector<unsigned char *> rows(MaxIndex);
double Zx, Zy;
double Zx2, Zy2;
int Iteration;
const double EscapeRadius = 2;
double ER2 = EscapeRadius * EscapeRadius;
for (iY = 0; iY < iYmax; iY++)
{
Cy = CyMin + iY * PixelHeight;
if (fabs(Cy) < PixelHeight / 2)
Cy = 0.0; /* Main antenna */
for (iX = 0; iX < iXmax; iX++)
{
Cx = CxMin + iX * PixelWidth;
/* initial value of orbit = critical point Z= 0 */
Zx = 0.0;
Zy = 0.0;
Zx2 = Zx * Zx;
Zy2 = Zy * Zy;
/* */
for (Iteration = 0; Iteration < IterationMax && ((Zx2 + Zy2) < ER2); Iteration++)
{
Zy = 2 * Zx * Zy + Cy;
Zx = Zx2 - Zy2 + Cx;
Zx2 = Zx * Zx;
Zy2 = Zy * Zy;
};
/* compute pixel color (24 bit = 3 bytes) */
if (Iteration == IterationMax)
{ /* interior of Mandelbrot set = black */
color[0] = 0;
color[1] = 0;
color[2] = 0;
}
else
{ /* exterior of Mandelbrot set = white */
color[0] = 255; /* Red*/
color[1] = 255; /* Green */
color[2] = 255; /* Blue */
};
rows[Index] = color;
Index++;
}
}
return rows;
}
Here is function to save it to file:
void saveToFile(vector<unsigned char *> matrix, char *filename)
{
char *comment = (char *)"# "; /* comment should start with # */
FILE *file;
file = fopen(filename, "wb"); /* b - binary mode */
fprintf(file, "P6\n %s\n %d\n %d\n %d\n", comment, iXmax, iYmax, MaxColorComponentValue);
for (int Index = 0; Index < MaxIndex; Index++)
{
fwrite(matrix[Index], 1, 3, file);
}
fclose(file);
}
Some global values and main loop:
const int iXmax = 1000;
const int iYmax = 1000;
const int MaxColorComponentValue = 255;
int const MaxIndex = (iXmax * iYmax) - 1;
int main()
{
clock_t start = clock();
vector<unsigned char *> image = drawMandelbrot();
clock_t stop = clock();
cout << (double(stop - start) / CLOCKS_PER_SEC) << " seconds\n";
char *filename = (char *)"new2.ppm";
saveToFile(image,filename);
return 0;
}
Problem is that generateMandelbrot() returns matrix like this:
image matrix
but it should be vector of elements looks like this which is actually color value:
color char
I know the problems is with color and image values types, but have any idea how it should look like.
Thanks!
This:
rows[Index] = color;
Is assigning the unsigned char * in your vector to the same array every time!
In other words it's like if I sell you ten cars and deliver the keys but they are all identical keys to the same car. Wouldn't you be upset?
Change your variables to use std::array:
using Color = std::array<unsigned char, 3>;
Color color;
vector<Color> rows(MaxIndex);
Now you have a vector of triples (Colors), instead of a vector of pointers that all point at the same triple.
I used the RTC, from an Arduino MKR 1300 with integrated RTC, as an alarm that will trigger a "boolean"(it's an integer) that will tell the loop to run a certain method every minute and then send some data every 5 minutes. It's on an active loop but the method to send data ONLY WORKS if it's inside the loop (no idea why). The problem is the RTC apparently is subtracting 1 second at every 8 hours or so after a few days the timing might come off and instead of sending data every xx:10:xx-xx:15:xx it might send data xx:09:xx-xx:14:xx.
Here's the code:
#include <EmonLib.h>
#include <RTCZero.h>
#include <MKRWAN.h>
EnergyMonitor emon1;
EnergyMonitor emon2;
EnergyMonitor emon3;
RTCZero rtc;
LoRaModem modem;
String appEui = "1234567891011121";
String appKey = "ffffffffffffffffffffffffffffffff";
/* INITIAL_TIME */
const byte seconds = 0;
const byte minutes = 0;
const byte hours = 0;
const byte day = 17;
const byte month = 12;
const byte year = 18;
byte second_alarm = 0;
byte minute_alarm = 0;
byte hour_alarm = 0;
byte INTERVAL = 60;
int SEND_LOOP = 5;
int totalKW;
int counter= 0;
int alarm_Triggered = 0;
void setup()
{
Serial.begin(115200);
if (!modem.begin(EU868)) {
Serial.println("Failed to start module");
while (1) {}
};
Serial.print("Your module version is: ");
Serial.println(modem.version());
Serial.print("Your device EUI is: ");
Serial.println(modem.deviceEUI());
Serial.println("Connecting");
int connected = modem.joinOTAA(appEui, appKey);
if (!connected) {
Serial.println("Something went wrong; are you indoor? Move near a window and retry");
while (1) {}
}
Serial.println("Connected");
// Set poll interval to 60 secs.
modem.minPollInterval(60);
analogReadResolution(9);
emon1.current(1, 53);
emon2.current(2, 53);
emon3.current(3, 53);
counter= 0;
rtc.begin(); // initialize RTC
rtc.setAlarmTime(hour_alarm, minute_alarm, second_alarm);
rtc.enableAlarm(rtc.MATCH_HHMMSS);
rtc.attachInterrupt(triggerAlarm);
// Set the time
rtc.setHours(hours);
rtc.setMinutes(minutes);
rtc.setSeconds(seconds);
// Set the date
rtc.setDay(day);
rtc.setMonth(month);
rtc.setYear(year);
}
void loop() {
if (alarm_Triggered == 1) {
dataMonitor();
alarm_Triggered = 0;
}
}
void dataMonitor() {
int totalWatt = 0;
unsigned long delay_send = 0;
int sending = 0;
double Irms1 = emon1.calcIrms(600);
if (Irms1 < 0.3) Irms1 = 0;
double Watt1 = Irms1 * 230;
double Irms2 = emon2.calcIrms(600);
if (Irms2 < 0.3) Irms2 = 0;
double Watt2 = Irms2 * 230;
double Irms3 = emon3.calcIrms(600);
if (Irms3 < 0.3) Irms3 = 0;
double Watt3 = Irms3 * 230;
totalWatt = Watt1 + Watt2 + Watt3;
totalKW = totalKW + totalWatt / 1000;
Serial.println(counter);
sendDataChecker(Irms1, Irms2, Irms3);
setAlarm();
counter= counter+ 1;
}
void sendDataChecker(double Irms1, double Irms2, double Irms3) {
if (counter== SEND_LOOP) {
double IrmsTotal = Irms1 + Irms2 + Irms3;
String msg = "{\"id\":\"avac_aud2\",\"kW\":" + String(totalKW) + ", \"current\":" + String(IrmsTotal) + "}";
int err;
Serial.println("Ready to Send");
modem.beginPacket();
modem.print(msg);
err = modem.endPacket(true);
Serial.println("Sent1");
if (err > 0) {
//message sent correctly
Serial.println("Sent");
counter= 0;
totalKW = 0;
} else {
Serial.println("ERR");
counter= 0;
}
}
}
void setAlarm() {
second_alarm += INTERVAL;
if (second_alarm >= 60) {
minute_alarm++;
second_alarm = 0;
}
if (minute_alarm >= 60) {
hour_alarm++;
minute_alarm = 0;
}
if (hour_alarm >= 24) {
hour_alarm = 0;
}
rtc.setAlarmTime(hour_alarm, minute_alarm, second_alarm);
}
void triggerAlarm() {
alarm_Triggered = 1;
}
I made a program in C++ which calculates the mandelbrot-set. Now I want to visualize it (save it in a picture). But when I try to save a 64k picture some problems come up. So what is the best way to save a picture of the pixels or at least to visual it?
Edit:
When I want to create a for Example 64K (61440 * 34560) image there will be the error "Access violation while writing at the position 0x0..." (originally on German and translated) and the program stops. This error appears with very high resolution. On lower resolutions the program works as it is supposed to.
#include <SFML\Graphics.hpp>
#include <stdlib.h>
#include <complex>
#include <cmath>
#include <thread>
//4K : 3840 * 2160
//8K : 7680 * 4320
//16K: 15360 * 8640
//32K: 30720 * 17280
//64K: 61440 * 34560
//128K:122880 * 69120
const unsigned long width = 61440; //should be dividable by ratioX & numberOfThreads!
const unsigned long height = 34560; //should be dividable by ratioY & numberOfThreads!
const unsigned int maxIterations = 500;
const unsigned int numberOfThreads = 6;
const int maxWidth = width / 3;
const int maxHeight = height / 2;
const int minWidth = -maxWidth * 2;
const int minHeight = -maxHeight;
const double ratioX = 3.0 / width;
const double ratioY = 2.0 / height;
sf::Image img = sf::Image();
int getsGreaterThan2(std::complex<double> z, int noIterations) {
double result;
std::complex<double> zTmp = z;
std::complex<double> c = z;
for (int i = 1; i != noIterations; i++) {
zTmp = std::pow(z, 2) + c;
if (zTmp == z) {
return 0;
}
z = std::pow(z, 2) + c;
result = std::sqrt(std::pow(z.real(), 2) + std::pow(z.imag(), 2));
if (result > 2) {
return i;
}
}
return 0;
}
void fillPixelArrayThreadFunc(int noThreads, int threadNr) { //threadNr ... starts from 0
double imgNumber;
double realNumber;
double tmp;
long startWidth = ((double)width) / noThreads * threadNr + minWidth;
long endWidth = startWidth + width / noThreads;
for (long x = startWidth; x < endWidth; x++) {
imgNumber = x * ratioX;
for (long y = minHeight; y < maxHeight; y++) {
realNumber = y * ratioY;
long xArray = x - minWidth;
long yArray = y - minHeight;
tmp = getsGreaterThan2(std::complex<double>(imgNumber, realNumber), maxIterations);
if (tmp == 0) {
img.setPixel(xArray, yArray, sf::Color(0, 0, 0, 255));
}
else {
img.setPixel(xArray, yArray, sf::Color(tmp / maxIterations * 128, tmp / maxIterations * 128, tmp / maxIterations * 255, 255));
}
}
}
}
int main() {
img.create(width, height, sf::Color::Black);
std::thread *threads = new std::thread[numberOfThreads];
for (int i = 0; i < numberOfThreads; i++) {
threads[i] = std::thread(std::bind(fillPixelArrayThreadFunc, numberOfThreads, i));
}
for (int i = 0; i < numberOfThreads; i++) {
threads[i].join();
}
img.saveToFile("filename.png");
return 1;
}
Your program fails during the call img.create(width, height, sf::Color::Black);.
When you step into the sf::Image::create function you end up here where the newPixels vector is created, this simply fails when width * height is too big as in your case:
////////////////////////////////////////////////////////////
void Image::create(unsigned int width, unsigned int height, const Color& color)
{
if (width && height)
{
// Create a new pixel buffer first for exception safety's sake
std::vector<Uint8> newPixels(width * height * 4);
^61440* ^34560 = 8'493'465'600 bytes !!
Conclusion: SFML cannot handle huge images.
I'm trying to mix some audio samples with the following algorithm:
short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes)
{
short* output = new short[numframes * 2]; // multiply 2 for channels
for (int sample = 0; sample < numframes * 2; ++sample)
{
for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
{
if (sample <= rawsources.at(sourceCount).frames * 2)
{
short outputSample = rawsources.at(sourceCount).data[sample];
output[sample] += outputSample;
}
}
}
// post mixing volume compression
for (int sample = 0; sample < numframes; ++sample)
{
output[sample] /= (float)rawsources.size();
}
return output;
}
I get the output I want except for the fact that when one of the sources are done, the other sources start playing louder. I know why this is but I don't know how to solve it properly.
Also, this is a screenshot from Audacity from the audio I output:
As you can see there's definitely something wrong. You can see that the audio hasn't got zero at the center anymore and you can see the audio getting louder once one of the sources are done playing.
Most of all I'd like to fix the volume problem but any other tweaks I can do are very appreciated!
Some extra info: I know that this code doesn't allow mono sources but that's ok. I'm only going to use stereo interleaved audio samples.
Usually mixing don't divide by the number of sources. This mean that mix a normal track with a mute track can halve its amplitude. If you want you can eventually normalize the track so that it is in his range.
The code is not tested, there may be errors:
#include <algorithm> // for std::max
#include <cmath> // for std::fabs
short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes)
{
// We can not use shorts immediately because can overflow
// I use floats because in the renormalization not have distortions
float *outputFloating = new float [numframes * 2];
// The maximum of the absolute value of the signal
float maximumOutput = 0;
for (int sample = 0; sample < numframes * 2; ++sample)
{
// makes sure that at the beginning is zero
outputFloating[sample] = 0;
for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
{
// I think that should be a '<'
if (sample < rawsources.at(sourceCount).frames * 2)
outputFloating[sample] += rawsources.at(sourceCount).data[sample];
}
// Calculates the maximum
maximumOutput = std::max (maximumOutput, std::fabs(outputFloating[sample]));
}
// A short buffer
short* output = new short [numframes * 2]; // multiply 2 for channels
float multiplier = maximumOutput > 32767 ? 32767 / maximumOutput : 1;
// Renormalize the track
for (int sample = 0; sample < numframes * 2; ++sample)
output[sample] = (short) (outputFloating[sample] * multiplier);
delete[] outputFloating;
return output;
}
Since you're adding up everything into a short before you divide, you're probably getting overflow. You need to add to an intermediary that's bigger. Also the final scaling shouldn't be dependent on the number of samples, it should be a constant - determine it before you call your function.
short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes, double gain = 0.5)
{
short* output = new short[numframes * 2]; // multiply 2 for channels
for (int sample = 0; sample < numframes * 2; ++sample)
{
long newSample = 0;
for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
{
if (sample <= rawsources.at(sourceCount).frames * 2)
{
short outputSample = rawsources.at(sourceCount).data[sample];
newSample += outputSample;
}
}
output[sample] = (short)(newSample * gain);
}
return output;
}
You don't really have to do the "post mixing volume compression". Simply add up all the sources and don't allow the sum to overflow. This should work:
short* FilterGenerator::mixSources(std::vector<RawData>rawsources, int numframes)
{
short* output = new short[numframes * 2]; // multiply 2 for channels
for (int sample = 0; sample < numframes * 2; ++sample)
{
long sum = 0;
for (int sourceCount = 0; sourceCount < rawsources.size(); ++sourceCount)
{
if (sample < rawsources.at(sourceCount).frames * 2)
{
short outputSample = rawsources.at(sourceCount).data[sample];
sum += outputSample;
output[sample] += outputSample;
}
if (sum > 32767) sum = 32767;
if (sum < -32768) sum = -32768;
output[sample] = (short)sum;
}
}
return output;
}