diff --git a/FMPX generator/FMPX generator.cpp b/FMPX generator/FMPX generator.cpp new file mode 100644 index 0000000..02e8285 --- /dev/null +++ b/FMPX generator/FMPX generator.cpp @@ -0,0 +1,146 @@ +#include +#include +#include "portaudio.h" +#include "PaDSPFunc.h" +#include "PaMPXFunc.h" + +#define INPUT_DEVICE_INDEX 16 +#define OUTPUT_DEVICE_INDEX 47 + + +int main() { + PaError err; + PaStream* inputStream, * outputStream; + PaStreamParameters inputParameters, outputParameters; + const PaDeviceInfo* inputInfo; + const PaDeviceInfo* outputInfo; + + Pa_Initialize(); + + inputInfo = Pa_GetDeviceInfo(INPUT_DEVICE_INDEX); + outputInfo = Pa_GetDeviceInfo(OUTPUT_DEVICE_INDEX); + + // Set input parameters + inputParameters.device = INPUT_DEVICE_INDEX; + inputParameters.channelCount = 2; + inputParameters.sampleFormat = paFloat32; // Example: float32 format + inputParameters.suggestedLatency = inputInfo->defaultHighInputLatency; + inputParameters.hostApiSpecificStreamInfo = NULL; + + // Set output parameters + outputParameters.device = OUTPUT_DEVICE_INDEX; + outputParameters.channelCount = 1; + outputParameters.sampleFormat = paFloat32; // Example: float32 format + outputParameters.suggestedLatency = outputInfo->defaultHighOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + // Open input audio stream + err = Pa_OpenStream(&inputStream, &inputParameters, NULL, 192000, + paFramesPerBufferUnspecified, paClipOff, NULL, NULL); + + if (err != paNoError) { + std::cerr << "PortAudio input stream error: " << Pa_GetErrorText(err) << std::endl; + Pa_Terminate(); + return -1; + } + + // Open output audio stream + err = Pa_OpenStream(&outputStream, NULL, &outputParameters, 192000, + paFramesPerBufferUnspecified, paClipOff, NULL, NULL); + + if (err != paNoError) { + std::cerr << "PortAudio output stream error: " << Pa_GetErrorText(err) << std::endl; + Pa_CloseStream(inputStream); + Pa_Terminate(); + return -1; + } + + // Start input audio stream + err = Pa_StartStream(inputStream); + if (err != paNoError) { + std::cerr << "PortAudio input stream start error: " << Pa_GetErrorText(err) << std::endl; + Pa_CloseStream(inputStream); + Pa_CloseStream(outputStream); + Pa_Terminate(); + return -1; + } + + // Start output audio stream + err = Pa_StartStream(outputStream); + if (err != paNoError) { + std::cerr << "PortAudio output stream start error: " << Pa_GetErrorText(err) << std::endl; + Pa_CloseStream(inputStream); + Pa_CloseStream(outputStream); + Pa_Terminate(); + return -1; + } + + const int framesPerBuffer = 4096; + float* buffer = new float[framesPerBuffer * inputParameters.channelCount]; + std::vector bands = { + {137, -20.2, 1, 3, 1, 100}, // Example parameters for Band 1 + {1147, -17.6, 0, 2, 1, 100}, // Example parameters for Band 2 + {6910, -24.8, 0, 2, 1, 100} // Example parameters for Band 3 + // Add more bands with their parameters + }; + + while (true) { + err = Pa_ReadStream(inputStream, buffer, framesPerBuffer); + if (err != paNoError) { + std::cout << "PortAudio input stream error: " << Pa_GetErrorText(err) << std::endl; + break; + } + + AudioBuffer audio{ buffer, framesPerBuffer * inputParameters.channelCount }; + multibandCompressor(audio, bands); + + //limiterProcess(buffer, framesPerBuffer, limiter); + + // Normalize the output to prevent extreme volume fluctuations + + float maxSample = 1.0f; + for (int i = 0; i < framesPerBuffer * inputParameters.channelCount; ++i) { + if (fabsf(buffer[i]) > maxSample) { + maxSample = fabsf(buffer[i]); + } + } + + if (maxSample > 1.0f) { + for (int i = 0; i < framesPerBuffer * inputParameters.channelCount; ++i) { + buffer[i] /= maxSample; + } + } + + // Merge stereo channels into mono + float* monoBuffer = new float[framesPerBuffer]; + float* subtractBuffer = new float[framesPerBuffer]; + for (int i = 0, j = 0; i < framesPerBuffer * inputParameters.channelCount; i += 2, ++j) { + monoBuffer[j] = 0.5f * (buffer[i] + buffer[i + 1]); // Average the two channels + } + for (int i = 0, j = 0; i < framesPerBuffer * inputParameters.channelCount; i += 2, ++j) { + subtractBuffer[j] = 0.5f * (buffer[i] - buffer[i + 1]); // Average the two channels + } + + + + err = Pa_WriteStream(outputStream, subtractBuffer, framesPerBuffer); + if (err != paNoError) { + std::cout << "PortAudio output stream error: " << Pa_GetErrorText(err) << std::endl; + break; + } + delete[] monoBuffer; + delete[] subtractBuffer; + + } + + delete[] buffer; + + // Stop and close streams + Pa_StopStream(inputStream); + Pa_CloseStream(inputStream); + Pa_StopStream(outputStream); + Pa_CloseStream(outputStream); + Pa_Terminate(); + + return 0; +} \ No newline at end of file diff --git a/FMPX generator/FMPX generator.vcxproj b/FMPX generator/FMPX generator.vcxproj new file mode 100644 index 0000000..3815ae4 --- /dev/null +++ b/FMPX generator/FMPX generator.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {51241d04-3bd0-4fa5-8bae-05c9618b01eb} + FMPXgenerator + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/FMPX generator/FMPX generator.vcxproj.filters b/FMPX generator/FMPX generator.vcxproj.filters new file mode 100644 index 0000000..b324938 --- /dev/null +++ b/FMPX generator/FMPX generator.vcxproj.filters @@ -0,0 +1,50 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + + Resource Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/FMPX generator/PaDSPFunc.cpp b/FMPX generator/PaDSPFunc.cpp new file mode 100644 index 0000000..f2307dc --- /dev/null +++ b/FMPX generator/PaDSPFunc.cpp @@ -0,0 +1,130 @@ +#include + +struct AudioBuffer { + float* data; // Audio data + int size; // Size of audio buffer +}; + +struct Band { + float frequency; // Center frequency of the band + float threshold; + float gain; + float ratio; + float attackTime; // Attack time in milliseconds + float releaseTime; // Release time in milliseconds + float currentGain; // Current gain level for dynamic changes + float envelope; // Envelope for dynamic changes +}; + +// Function to process a single band with dynamic gain changes (attack and release) +void processBandWithDynamicGain(float* buffer, int size, Band& band) { + const float attackCoef = 1.0f - expf(-1.0f / (0.001f * band.attackTime * 44100.0f)); // Calculate attack coefficient + const float releaseCoef = 1.0f - expf(-1.0f / (0.001f * band.releaseTime * 44100.0f)); // Calculate release coefficient + + for (int i = 0; i < size; ++i) { + float input = buffer[i]; + + // Envelope follower for dynamic gain adjustment (simulating RMS detection) + float envelopeLevel = fabsf(input); + + if (envelopeLevel > band.envelope) { + band.envelope += attackCoef * (envelopeLevel - band.envelope); + } + else { + band.envelope += releaseCoef * (envelopeLevel - band.envelope); + } + + // Apply compression with dynamic gain based on negative threshold + float gainReduction = 1.0f; + if (band.envelope < band.threshold) { // Check if the envelope level is below the negative threshold + float excessDB = 20.0f * log10f(band.threshold / band.envelope); + gainReduction = band.gain + (1.0f - band.ratio) * excessDB; // Calculate gain reduction based on ratio + } + + buffer[i] *= gainReduction; + } +} + +// Function to split audio into bands and compress each band with dynamic gain +void multibandCompressor(AudioBuffer& audio, std::vector& bands) { + std::vector> bandBuffers(bands.size(), std::vector(audio.size)); + + // Split audio into frequency bands + for (int i = 0; i < audio.size; ++i) { + for (size_t j = 0; j < bands.size(); ++j) { + // Split audio into frequency bands (in this simplified example) + // Replace with appropriate band-splitting logic + bandBuffers[j][i] = audio.data[i]; // Simulated band splitting + } + } + + // Process each band separately with dynamic gain changes + for (size_t i = 0; i < bands.size(); ++i) { + processBandWithDynamicGain(bandBuffers[i].data(), audio.size, bands[i]); + } + + // Recombine bands + for (int i = 0; i < audio.size; ++i) { + float mixedValue = 0.0f; + for (size_t j = 0; j < bands.size(); ++j) { + mixedValue += bandBuffers[j][i]; // Simulated band recombination + } + audio.data[i] = mixedValue; // Replace with proper recombination logic + } +} + +void audioCompressor(float* audioBuffer, int bufferSize, float threshold, float ratio) { + for (int i = 0; i < bufferSize; ++i) { + // Apply compression algorithm to each sample + if (audioBuffer[i] > threshold) { + audioBuffer[i] = threshold + (audioBuffer[i] - threshold) / ratio; + } + else if (audioBuffer[i] < -threshold) { + audioBuffer[i] = -threshold + (audioBuffer[i] + threshold) / ratio; + } + } +} + +struct Limiter { + float threshold; + float margin; + float attackTime; // Attack time in milliseconds + float releaseTime; // Release time in milliseconds + float ratio; + float currentGain; // Current gain level for dynamic changes + float envelope; // Envelope for dynamic changes +}; + +void limiterProcess(float* buffer, int size, Limiter& limiter) { + const float attackCoef = 1.0f - expf(-1.0f / (0.001f * limiter.attackTime * 44100.0f)); // Calculate attack coefficient + const float releaseCoef = 1.0f - expf(-1.0f / (0.001f * limiter.releaseTime * 44100.0f)); // Calculate release coefficient + + for (int i = 0; i < size; ++i) { + float input = buffer[i]; + + // Envelope follower for dynamic gain adjustment (simulating RMS detection) + float envelopeLevel = fabsf(input); + + if (envelopeLevel > limiter.envelope) { + limiter.envelope += attackCoef * (envelopeLevel - limiter.envelope); + } + else { + limiter.envelope += releaseCoef * (envelopeLevel - limiter.envelope); + } + + // Apply limiting with dynamic gain + if (limiter.envelope > limiter.threshold) { + float reductionDB = limiter.threshold + limiter.margin - limiter.envelope; + float gainReduction = powf(10.0f, reductionDB / 20.0f); // Convert dB reduction to linear gain + + // Apply compression with dynamic gain + float softKnee = 1.0f / limiter.ratio; // Soft knee effect + float softKneeCoefficient = 1.0f - expf(-softKnee); // Soft knee coefficient + + float gain = 1.0f / (1.0f + softKneeCoefficient * (gainReduction - 1.0f)); + float limitedGain = (gainReduction < gain) ? gainReduction : gain; + + buffer[i] *= limitedGain; + } + } +} \ No newline at end of file diff --git a/FMPX generator/PaDSPFunc.h b/FMPX generator/PaDSPFunc.h new file mode 100644 index 0000000..e480dc8 --- /dev/null +++ b/FMPX generator/PaDSPFunc.h @@ -0,0 +1,40 @@ +#ifndef PADSPFUNC_H +#define PADSPFUNC_H +#include + +void audioCompressor(float* audioBuffer, int bufferSize, float threshold, float ratio); + +struct AudioBuffer { + float* data; + int size; +}; + +struct Band { + float frequency; + float threshold; + float gain; + float ratio; + float attackTime; + float releaseTime; + float currentGain; + float envelope; +}; + +void processBandWithDynamicGain(float* buffer, int size, Band& band); +void multibandCompressor(AudioBuffer& audio, std::vector& bands); + + +struct Limiter { + float threshold; + float margin; + float attackTime; // Attack time in milliseconds + float releaseTime; // Release time in milliseconds + float ratio; + float currentGain; // Current gain level for dynamic changes + float envelope; // Envelope for dynamic changes +}; + +void limiterProcess(float* buffer, int size, Limiter& limiter); + + +#endif diff --git a/FMPX generator/PaMPXFunc.cpp b/FMPX generator/PaMPXFunc.cpp new file mode 100644 index 0000000..7156fcf --- /dev/null +++ b/FMPX generator/PaMPXFunc.cpp @@ -0,0 +1,40 @@ +#include +#include + +// Function to mix left and right channels (L + R) +std::vector mixChannels(const std::vector& leftChannel, const std::vector& rightChannel) { + // Ensure both channels have the same size + if (leftChannel.size() != rightChannel.size()) { + std::cerr << "Channels have different sizes. Mixing aborted." << std::endl; + return {}; + } + + std::vector mixedChannel; + mixedChannel.reserve(leftChannel.size()); // Reserve space for the mixed channel + + // Perform mixing (L + R) + for (size_t i = 0; i < leftChannel.size(); ++i) { + mixedChannel.push_back(leftChannel[i] + rightChannel[i]); + } + + return mixedChannel; +} + +// Function to subtract left from right channel (L - R) +std::vector subtractChannels(const std::vector& leftChannel, const std::vector& rightChannel) { + // Ensure both channels have the same size + if (leftChannel.size() != rightChannel.size()) { + std::cerr << "Channels have different sizes. Subtraction aborted." << std::endl; + return {}; + } + + std::vector subtractedChannel; + subtractedChannel.reserve(leftChannel.size()); // Reserve space for the subtracted channel + + // Perform subtraction (L - R) + for (size_t i = 0; i < leftChannel.size(); ++i) { + subtractedChannel.push_back(leftChannel[i] - rightChannel[i]); + } + + return subtractedChannel; +} \ No newline at end of file diff --git a/FMPX generator/PaMPXFunc.h b/FMPX generator/PaMPXFunc.h new file mode 100644 index 0000000..cc8aebe --- /dev/null +++ b/FMPX generator/PaMPXFunc.h @@ -0,0 +1,9 @@ +#ifndef PAMPXFUNC_H +#define PAMPXFUNC_H + +#include + +std::vector mixChannels(const std::vector& leftChannel, const std::vector& rightChannel); +std::vector subtractChannels(const std::vector& leftChannel, const std::vector& rightChannel); + +#endif diff --git a/FMPX generator/PaProcessFunc.cpp b/FMPX generator/PaProcessFunc.cpp new file mode 100644 index 0000000..7a9a24e --- /dev/null +++ b/FMPX generator/PaProcessFunc.cpp @@ -0,0 +1,11 @@ +void audioCompressor(float* audioBuffer, int bufferSize, float threshold, float ratio) { + for (int i = 0; i < bufferSize; ++i) { + // Apply compression algorithm to each sample + if (audioBuffer[i] > threshold) { + audioBuffer[i] = threshold + (audioBuffer[i] - threshold) / ratio; + } + else if (audioBuffer[i] < -threshold) { + audioBuffer[i] = -threshold + (audioBuffer[i] + threshold) / ratio; + } + } +} \ No newline at end of file diff --git a/FMPX generator/packages.config b/FMPX generator/packages.config new file mode 100644 index 0000000..4648ce2 --- /dev/null +++ b/FMPX generator/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/FMPXGEN.sln b/FMPXGEN.sln new file mode 100644 index 0000000..4afce53 --- /dev/null +++ b/FMPXGEN.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33122.133 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FMPX generator", "FMPX generator\FMPX generator.vcxproj", "{51241D04-3BD0-4FA5-8BAE-05C9618B01EB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Debug|x64.ActiveCfg = Debug|x64 + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Debug|x64.Build.0 = Debug|x64 + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Debug|x86.ActiveCfg = Debug|Win32 + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Debug|x86.Build.0 = Debug|Win32 + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Release|x64.ActiveCfg = Release|x64 + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Release|x64.Build.0 = Release|x64 + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Release|x86.ActiveCfg = Release|Win32 + {51241D04-3BD0-4FA5-8BAE-05C9618B01EB}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C351DBD9-F255-499D-957D-CB8219086FD5} + EndGlobalSection +EndGlobal