0

I am generating from C++ code various forms of a PCM sound wave using composed harmonics of sine functions, then after importing file in Audacity to see the result, import is done at a maximum sample rate Audacity can support for raw data files (signed 16 bits, 384000Hz) just to spot possible errors in sampled harmonics. The problem what I have spotted is that after 5000 samples the sawtooth wave differ in length than the original sin base and the square one (these 2 are aligned in the final width). Where the length error is coming for the sawtooth wave I really do not understand.

Code:

#include <iostream>
#include <fstream>
#include <cmath>

typedef char               sint8;
typedef unsigned char      uint8;
typedef signed short int   sint16;
typedef unsigned short int uint16;
typedef unsigned int       uint32;

using namespace std;

#define PI 3.14159265F

#define DEG2RAD(value) ((float)(value) * (PI/180.F))

// Just for debugging LSB and MSB
void dec2bin(signed long int number, unsigned short size)
{
    int c, k;
    for (c = (size*8)-1; c >= 0; c--)
    {
        k = number >> c;
        if (k & 1)
            printf("1");
        else
            printf("0");
        if (c % 4 == 0)
            printf(" ");
    }
    printf("\n");
}

float generateSquareWave(uint32 degreeValue, uint32 harmonics, uint32 frequency)
{
  float resultSinSum = 0.F;

  for(uint32 degreeIndex = 0x00000001U; degreeIndex <= 0x00000002U*harmonics; degreeIndex += 0x00000002U)
  {
      resultSinSum += ((1.F/(float)(degreeIndex))*sin(((float)(frequency))*((float)(degreeIndex))*DEG2RAD(degreeValue)));
  }

  return resultSinSum;
}

float generateSawTooth(uint32 degreeValue, uint32 harmonics, uint32 frequency)
{
  float resultSinSum = 0.F;

  for(uint32 degreeIndex = 0x00000001U; degreeIndex <= 0x00000002U*harmonics; degreeIndex += 0x00000001U)
  {
      resultSinSum += ((1.F/(float)(degreeIndex))*sin(((float)(frequency))*((float)(degreeIndex))*DEG2RAD(degreeValue)));
  }

  return -(0.615F * resultSinSum);
}

int main()
{
  const uint32 WAVE_CYCLES = 5000U;
  const uint32 DEGREE_PER_CYCLE = 360U;
  const uint32 NUMBER_OF_SAMPLES = WAVE_CYCLES * DEGREE_PER_CYCLE;

  sint8 functionSamples[NUMBER_OF_SAMPLES] = {};

  ofstream myfile;

  myfile.open("sound.raw");

  for(uint32 degreeIndex = 0x00000000; degreeIndex <= NUMBER_OF_SAMPLES; degreeIndex += 0x00000001U)
  {
      // ORIGINAL: float soundFunction = sin(DEG2RAD(degreeIndex));
      //float soundFunction = cos(DEG2RAD(degreeIndex));
      //float soundFunction = sqrt(DEG2RAD(degreeIndex));

      //float soundFunction = generateSquareWave(degreeIndex, 16U, 1U);

      float soundFunction = generateSawTooth(degreeIndex, 16U, 1U);

      soundFunction = soundFunction * 32768;

      if( soundFunction > 32767 ) soundFunction = 32767;
      if( soundFunction < -32768 ) soundFunction = -32768;

      // Write LSB 16b int PCM from float32
      functionSamples[degreeIndex] = static_cast<sint8>(static_cast<sint16>(soundFunction) >> 8);
      functionSamples[degreeIndex+1] = static_cast<sint8>(soundFunction);
  }

  myfile.write(&functionSamples[0], NUMBER_OF_SAMPLES);

  myfile.close();

  return 0;
}

First 5 cycles are aligned in width: enter image description here

After 5000 cycles: Seems that sawtooth wave have a bigger number of periods, but is same as the other 2 initial waves just the length in the cycle width differ for which I don't have a explanation. enter image description here

I also overlap waves in the Google plot view to spot the error but no error: enter image description here

LXSoft
  • 587
  • 5
  • 25
  • 1
    First of all, please don't tag languages you're not programming in. You're programming in C++ so that should be the only language tag. Secondly, don't define your own fixed-width integer types, use [the standard types from ``](https://en.cppreference.com/w/cpp/header/cstdint). And what with all the long hexadecimal constants? What wrong with plain `0`, `1` or `2` etc.? – Some programmer dude May 26 '22 at 09:02
  • 1
    Could be a issue with floating point precision. – fabian May 26 '22 at 09:07
  • @fabian I would expect that too, but I don't understand why rounding error does not apply for sine and for the square wave same they are also using floating precision the same way. – LXSoft May 26 '22 at 09:13
  • 2
    Are you sure that you are comparing the correct files and that those aren't modified by Audacity? You have 5000 cycles of 360 samples (which BTW you could just copy 5000 times) at 384kHz, it means that all the waves should last 4.6875 seconds. So my first concern is why two of those only last half that duration. – Bob__ May 26 '22 at 14:37
  • @Bob__ Each individual wave function is created exactly the same with same degree period with 0 offset for all of them. The problem maybe is in Audacity rendering or the floating pointing precision because are just 7 cycles more than the square and pure sine. This means around ~0.014% in compounding error, but why the other two waves are behaving normally? I don't have any explanation... PS.: I did again the import and the results are same. – LXSoft May 27 '22 at 09:50
  • In the posted code, `for(uint32 degreeIndex = 0U; degreeIndex <= NUMBER_OF_SAMPLES; degreeIndex += 1U)` accesses out of the bounds of the array, due to the `<=` instead of `<`. Moreover, `functionSamples[degreeIndex+1] = ...` (other than a final out of bound) writes values in the array which are all *overwritten* at the next iteration by the previous line `functionSamples[degreeIndex] = ...` (remember the `degreeIndex += 1U`). Could you please start fixing that? – Bob__ May 27 '22 at 13:33
  • 1
    Another interesting detail is in the second screenshot of Audacity. The sawtooth wave seems to be almost 14 cycles longer than the other two. It happens that 5000 / 360 = 13.889, so that just 1 extra sample for every cycle could explain that length. An off by one error can be seen in the main loop (as already mentioned, but it's only by 1), which makes me wonder again if this is really the program used to generate *all* the data or if a different version was used for each wave. – Bob__ May 27 '22 at 19:43

0 Answers0