Static noise in generated sine wave pcm sound - c++

This is very similar to a question here but I don't seem to be able to apply the solution.
I have a code that samples a sine wave and writes it into a pcm file. When I listen to it with ffplay, there is some static noise that I don't know where it comes from. Based on the solution in the mentioned post, I use a binary file for writting out and I make sure I play the file with signed 8 bit format.
This is the code I use:
int createSineWavePCM(int freq, int sample_rate) {
char out_name[100];
sprintf(out_name, "../sine_freq%d_sr%d.pcm", freq, sample_rate);
ofstream outfile(out_name, ios::binary);
char data[1000000];
for (int j = 0 ; j < 1000000 ; ++j) {
double ll = 50.0L * sin((2.0L * M_PIl * j * freq / sample_rate));
data[j] = ll;
}
outfile.write(data, sizeof data);
outfile.close();
cout << "Stored sine wave pcm file in " << out_name << endl;
return 0;
}
I use freq = 440 and sample_rate = 44100, and then I play with:
ffplay {pcm_file} -f s8 -sample_rate 44100
Any ideas on what may cause the static noise?

The expression in the sin function looks dubious. Are all the components of type int or long? You wrote 2.0L, and I'm surprised that parses, but L usually converts a number to a long. Also it seems like M_PI has an l appended which would possibly also make that a long. If this is the case, the division being performed freq / sample_rate could well be integer division.

Related

Detecting linear interpolation of two frequnecies on embedded system

I am trying to recognise a sequence of audio frames on an embedded system - an audio frame being a frequency or interpolation of two frequencies for a variable amount of time. I know the sounds I am trying to recognise (i.e. the start and end frequencies which are being linearly interpolated and the duration of each audio frame), but they are produced by a another embedded system so the microphone and speaker are cheap and somewhat inaccurate. The output is a square wave. Any suggestions how to go about doing this?
What I am trying to do now is to use FFT to get the magnitude of all frequencies, detect the peaks, look at the detection duration/2 ms ago and check if that somewhat matches an audio frame, and finally just checking if any sound I am looking for matched the sequence.
So far I used the FFT to process the microphone input - after applying a Hann window - and then assigning each frequency bin a coefficient that it's a peak based on how many standard deviations is away from the mean. This hasn't worked great since it thought there are peaks when it was silence in the room. Any ideas on how to more accurately detect the peaks? Also I think there are a lot of harmonics because of the square wave / interpolation? Can I do harmonic product spectrum if the peaks don't really line up at double the frequency?
Here I graphed noise (almost silent room) with somewhere in the interpolation of 2226 and 1624 Hz.
https://i.stack.imgur.com/R5Gs2.png
I sample at 91 microseconds -> 10989 Hz. Should I sample more often?
I added here samples of how the interpolation sounds when recorded on my laptop and on the embedded system.
https://easyupload.io/m/5l72b0
#define MIC_SAMPLE_RATE 10989 // Hz
#define AUDIO_SAMPLES_NUMBER 1024
MicroBitAudioProcessor::MicroBitAudioProcessor(DataSource& source) : audiostream(source)
{
arm_rfft_fast_init_f32(&fft_instance, AUDIO_SAMPLES_NUMBER);
buf = (float *)malloc(sizeof(float) * (AUDIO_SAMPLES_NUMBER * 2));
output = (float *)malloc(sizeof(float) * AUDIO_SAMPLES_NUMBER);
mag = (float *)malloc(sizeof(float) * AUDIO_SAMPLES_NUMBER / 2);
}
float henn(int i){
return 0.5 * (1 - arm_cos_f32(2 * 3.14159265 * i / AUDIO_SAMPLES_NUMBER));
}
int MicroBitAudioProcessor::pullRequest()
{
int s;
int result;
auto mic_samples = audiostream.pull();
if (!recording)
return DEVICE_OK;
int8_t *data = (int8_t *) &mic_samples[0];
int samples = mic_samples.length() / 2;
for (int i=0; i < samples; i++)
{
s = (int) *data;
result = s;
data++;
buf[(position++)] = (float)result;
if (position % AUDIO_SAMPLES_NUMBER == 0)
{
position = 0;
float maxValue = 0;
uint32_t index = 0;
// Apply a Henn window
for(int i=0; i< AUDIO_SAMPLES_NUMBER; i++)
buf[i] *= henn(i);
arm_rfft_fast_f32(&fft_instance, buf, output, 0);
arm_cmplx_mag_f32(output, mag, AUDIO_SAMPLES_NUMBER / 2);
}
}
return DEVICE_OK;
}
uint32_t frequencyToIndex(int freq) {
return (freq / ((uint32_t)MIC_SAMPLE_RATE / AUDIO_SAMPLES_NUMBER));
}
float MicroBitAudioProcessor::getFrequencyIntensity(int freq){
uint32_t index = frequencyToIndex(freq);
if (index <= 0 || index >= (AUDIO_SAMPLES_NUMBER / 2) - 1) return 0;
return mag[index];
}

Mixing audio channels

I am implementing an audio channel mixer and using Viktor T. Toth's algorithm. Trying to mix two audio channel streams.
In the code, quantization_ is the byte representation of the bit depth of a channel. My mix function, takes a pointer to destination and source uint8_t buffers, mixes two channels and writes into the destination buffer. Because I am taking data in a uint8_t buffer, doing that addition, division, and multiplication operations to get the actual 8, 16 or 24-bit samples and convert them again to 8-bit.
Generally, it gives the expected output sample values. However, some samples turn out to have near 0 value as they are not supposed to be when I look the output in Audacity. In the screenshot, bottom 2 signals are two mono channels and the top one is the mixed channel. It can be seen that there are some very low values, especially in the middle.
Below, is my mix function;
void audio_mixer::mix(uint8_t* dest, const uint8_t* source)
{
uint64_t mixed_sample = 0;
uint64_t dest_sample = 0;
uint64_t source_sample = 0;
uint64_t factor = 0;
for (int i = 0; i < channel_size_; ++i)
{
dest_sample = 0;
source_sample = 0;
factor = 1;
for (int j = 0; j < quantization_; ++j)
{
dest_sample += factor * static_cast<uint64_t>(*dest++);
source_sample += factor * static_cast<uint64_t>(*source++);
factor = factor * 256;
}
mixed_sample = (dest_sample + source_sample) - (dest_sample * source_sample / factor);
dest -= quantization_;
for (int k = 0; k < quantization_; ++k)
{
*dest++ = static_cast<uint8_t>(mixed_sample % 256);
mixed_sample = mixed_sample / 256;
}
}
}
It seems like you aren't treating the signed audio samples correctly. The horizontal line should be zero voltage from your audio signal.
If you look at the positive voltage audio samples they obey your equation correctly (except for the peak values in the center). The negative values are being compressed which makes me feel like they are being treated as small positive voltages instead of negative voltages.
In other words, maybe those unsigned ints should be signed ints so the top bit indicates the voltage polarity and you can have audio samples in the range +127 to -128.
Those peak values in the center seem like they are wrapping around modulo 255 which would be the peak value for an unsigned byte representation of your audio. I'm not sure how this would happen but it seems related to the unsigned vs signed signals.
Maybe you should try the other formula Viktor provided in his document:
Z = 2(A+B) - (AB/128) - 256

Data from wav file are between -1 and 1, c++, sndfile

I am trying to read data from .wav and put it to fft.
To read wav file I am using sndfile library.
SNDFILE* infile;
SF_INFO sfinfo ;
memset (&sfinfo, 0, sizeof (sfinfo)) ;
infile = sf_open ("sound.wav", SFM_READ, &sfinfo);
double data [BUF_SIZE];
while (readcount = (int)sf_readf_double (infile, data, BUF_SIZE))
{
for (int i = 0; i < readcount; i++)
{
cout << data[i] << " ";
}
}
But every values in this (and other files) are between (-1 ; 1).
Is this correct? Why every values are so small? I was expected to read amplitude in time domain (volume of sound).
This is the canonical format of floating point samples. With float values, you get full 32-bit precision. Clipping is also easy to represent. If a sample value is higher than 1 or lower than -1, it means the sample clipped. With integer values, there's no way to know that.
Floating point is also an easy sample format to apply operations to. Mixing for example is trivial (you just add the sample values together.)
So even if it looks weird at first, it is the best format for audio sample representation. Once you applied the operations you need to the float values, you then convert them to the format you want for output (like 16-bit integers.) This operation is trivial. Here's a function that converts and clips float samples to any known integer sample format in use today:
#include <limits>
/* Convert and clip a float sample to an integer sample. This works for
* all usual integer sample types (8-bit, 16-bit, 32-bit, signed or
* unsigned.)
*/
template <typename T>
T floatSampleToInt(float src) noexcept
{
if (src >= 1.f)
return std::numeric_limits<T>::max();
if (src < -1.f)
return std::numeric_limits<T>::min();
return src * (float)(1UL << (sizeof(T) * 8 - 1))
+ ((float)(1UL << (sizeof(T) * 8 - 1))
+ (float)std::numeric_limits<T>::min());
}
If you want to convert a float sample to a signed 16-bit integer sample for example, you do:
int16_t intSample = floatSampleToInt<int16_t>(floatSample);
Note that 24-bit integer samples are covered by 32-bit. A 32-bit sample is also a valid 24-bit sample; its lower 8 bits are just truncated.

Raspberry Pi generate tone with PiFM

While back I came across PiFM and decided to learn a bit about audio and modulation. I am trying to write an AFSK modulator, but first I wanted to generate pure tones, like 1000Hz. I am using code from the PiFM project (https://github.com/rm-hull/pifm/blob/master/pifm.cpp) which reads in a WAV file and shoots it out over RF. I want to do the same but I want to shoot out pure tones over RF.
Here is one of my attempts:
void playSineWave(float frequency, int duration, float samplerate)
{
SampleSink* ss;
ss = new Outputter(samplerate);
int bufferLen = duration * samplerate;
float* buffer = new float[bufferLen];
for (int i = 0; i < bufferLen; i++) {
float amplitude = 6000;
buffer[i] = amplitude * sin( (2.f * float(M_PI) * i * frequency) / samplerate );
}
cout << "Buffer length: " << bufferLen << endl;
ss->consume(buffer, bufferLen);
delete [] buffer;
}
I would use it as such playSineWave(1000, 5, 22050); to play 1000Hz tone for 5 seconds. But I get either nothing or noise. Can you guys suggest how to fix it or perhaps some good reading material?
Edit: Changed code to fix issue with amplitude. Still no tone.
I'm not sure if it's intentional to make amplitude change over time. Anyhow, i / bufferLen * 32760 is evaluated as (i / bufferLen) * 32760 and i / bufferLen is going to be 0 for all i smaller than bufferLen (look up integer division in C/C++), which is the case because of for (int i = 0; i < bufferLen; i++).

Export buffer to WAV in C++

I have a simple program that creates a single cycle sine wave and puts the float numbers to a buffer. Then this is exported to a text file.
But I want to be able to export it to a WAV file (24 bit). Is there a simple way of doing it like on the text file?
Here is the code I have so far:
#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;
int main ()
{
long double pi = 3.14159265359; // Declaration of PI
ofstream textfile; // Text object
textfile.open("sine.txt"); // Creating the txt
double samplerate = 44100.00; // Sample rate
double frequency = 200.00; // Frequency
int bufferSize = (1/frequency)*samplerate; // Buffer size
double buffer[bufferSize]; // Buffer
for (int i = 0; i <= (1/frequency)*samplerate; ++i) // Single cycle
{
buffer[i] = sin(frequency * (2 * pi) * i / samplerate); // Putting into buffer the float values
textfile << buffer[i] << endl; // Exporting to txt
}
textfile.close(); // Closing the txt
return 0; // Success
}
First you need to open the stream for binary.
ofstream stream;
stream.open("sine.wav", ios::out | ios::binary);
Next you'll need to write out a wave header. You can search to find the details of the wave file format. The important bits are the sample rate, bit depth, and length of the data.
int bufferSize = (1/frequency)*samplerate;
stream.write("RIFF", 4); // RIFF chunk
write<int>(stream, 36 + bufferSize*sizeof(int)); // RIFF chunk size in bytes
stream.write("WAVE", 4); // WAVE chunk
stream.write("fmt ", 4); // fmt chunk
write32(stream, 16); // size of fmt chunk
write16(stream, 1); // Format = PCM
write16(stream, 1); // # of Channels
write32(stream, samplerate); // Sample Rate
write32(stream, samplerate*sizeof(int)); // Byte rate
write16(stream, sizeof(int)); // Frame size
write16(stream, 24); // Bits per sample
stream.write("data", 4); // data chunk
write32(stream, bufferSize*sizeof(int)); // data chunk size in bytes
Now that the header is out of the way, you'll just need to modify your loop to first convert the double (-1.0,1.0) samples into 32-bit signed int. Truncate the bottom 8-bits since you only want 24-bit and then write out the data. Just so you know, it is common practice to store 24-bit samples inside of a 32-bit word because it is much easier to stride through using native types.
for (int i = 0; i < bufferSize; ++i) // Single cycle
{
double tmp = sin(frequency * (2 * pi) * i / samplerate);
int intVal = (int)(tmp * 2147483647.0) & 0xffffff00;
stream << intVal;
}
A couple other things:
1) I don't know how you weren't overflowing buffer by using the <= in your loop. I changed it to a <.
2) Again regarding the buffer size. I'm not sure if you are aware but you can't have a repeated waveform represented by a single cycle for all frequencies. What I mean is that for most frequencies if you use this code and expect to play the waveform repeated, you're going to hear a glitch on every cycle. It'll work for nice synchronous frequencies like 1kHz because there will be exactly 48 samples per cycle and it will come around to exactly the same phase. 999.9 Hz will be a different story though.