Author Topic: How can I generate a triangle wave with "BASS_SampleCreate"?  (Read 779 times)

riku

  • Posts: 17
Hello.

I want to generate a triangular wave sample for 1 second with "BASS_SampleCreate".
How should I do it?
I'm calling "bass.dll" directly in C #.
Thank you.

Ian @ un4seen

  • Administrator
  • Posts: 21991
The sine wave example in the BASS_SampleCreate documentation could be modified for a triangle wave like this:

Code: [Select]
HSAMPLE sample = BASS_SampleCreate(256, 2 * 28160, 1, 1, BASS_SAMPLE_LOOP | BASS_SAMPLE_OVER_POS); // create sample
short data[128]; // data buffer
int a;
for (a = 0; a < 32; a++) {
data[a] = 32767 * a / 32;
data[32 + a] = 32767 - data[a];
data[64 + a] = -data[a];
data[96 + a] = -32767 + data[a];
}
BASS_SampleSetData(sample, data); // set the sample's data

If you want the sample to be a certain length (instead of looping until stopped), you can repeat/duplicate the waveform until it reaches that length.
« Last Edit: 21 May '19 - 17:29 by Ian @ un4seen »

riku

  • Posts: 17
Hi.

Thank you for your advice.

How do I specify frequency (Hz) and beep length (milliseconds)?

Best regards.

Ian @ un4seen

  • Administrator
  • Posts: 21991
Here's a version that lets you set the frequency and length:

Code: [Select]
int period = 64; // wave period in samples (should be multiple of 4)
int cycles = seconds * frequency; // sample length in cycles
HSAMPLE sample = BASS_SampleCreate(cycles * period * 2, frequency * period, 1, 1, 0); // create sample
short *data = new short[cycles * period]; // data buffer
int a;
// generate a cycle
int period4 = period / 4;
for (a = 0; a < period4; a++) {
data[a] = 32767 * a / period4;
data[period4 + a] = 32767 - data[a];
data[2 * period4 + a] = -data[a];
data[3 * period4 + a] = -32767 + data[a];
}
// repeat the cycles
for (a = 1; a < cycles; a++)
memcpy(data + a * period, data, period * 2);
BASS_SampleSetData(sample, data); // set the sample's data
delete[] data;
« Last Edit: 22 May '19 - 12:08 by Ian @ un4seen »

Somepony

  • Posts: 7
Isnt there should be
Code: [Select]
memcpy(data + a * period * 2, data, period * 2)?

Also, this code doesn't allow set high frequencies, since BASS_SampleCreate function fails with unknown error (-1) if second param freq is more than 200000.
« Last Edit: 22 May '19 - 00:33 by Somepony »

riku

  • Posts: 17
Thank you for your advice.

But I have something to ask.

Code: [Select]
short * data = new float [cycles * period];
does not match the type of the array to be assigned to the type of the array actually created.
Also, with
Code: [Select]
float * data = new float [cycles * period];
I think that memcpy does not go well, but how about that?

Best regards.
« Last Edit: 22 May '19 - 10:12 by riku »

Somepony

  • Posts: 7
I think there should be short instead of float

Ian @ un4seen

  • Administrator
  • Posts: 21991
Isnt there should be
Code: [Select]
memcpy(data + a * period * 2, data, period * 2)?

No, "data" is a "short" pointer, so the "* 2" is automatic. Another (perhaps clearer) way to write it is this:

Code: [Select]
memcpy(&data[a * period], data, period * sizeof(*data));

Also, this code doesn't allow set high frequencies, since BASS_SampleCreate function fails with unknown error (-1) if second param freq is more than 200000.

Are you using DirectSound output? That does indeed have a 200000 Hz sample rate limit. There should be no such limit if you use WASAPI output (so that BASS handles mixing) instead. You can also lower the "period" value to bring the required sample rate down.

Code: [Select]
short * data = new float [cycles * period];
does not match the type of the array to be assigned to the type of the array actually created.

Oops! That's a typo. That post has been updated now.

riku

  • Posts: 17
Hi.

I am coding in C #. How should I write the following part?

Code: [Select]
memcpy(data + a * period, data, period * 2);

Could you give me some advice?

Best regards.

Ian @ un4seen

  • Administrator
  • Posts: 21991
I'm not a .Net user myself, so I'm not certain, but I think you could use the Array.Copy method. Perhaps something like this:

Code: [Select]
Array.Copy(data, 0, data, a * period, period);

riku

  • Posts: 17
Thank you for your advice.

Code: [Select]
Array.Copy(data, 0, data, a * period, period);

It worked well with the above code.

But I have one question.
When specifying the frequency and the number of seconds with decimals, int type does not work well, but how should I fix it?
C code is fine, so can you tell me?

Best regards.

Ian @ un4seen

  • Administrator
  • Posts: 21991
It should be fine for the "seconds" and "frequency" values to be floating-point. The sample's length will be rounded down to a whole cycle (and rate rounded to a whole Hz), but the difference will be small and unnoticeable. If you like, you can set a sample channel (from BASS_SampleGetChannel) to use a fractional rate via the BASS_ATTRIB_FREQ attribute:

Code: [Select]
BASS_ChannelSetAttribute(channel, BASS_ATTRIB_FREQ, frequency * period);

riku

  • Posts: 17
Thank you for your advice.

I wrote the following code.

Code: [Select]
float cycles = seconds * frequency;
HSAMPLE sample = BASS_SampleCreate(cycles * period * 2, frequency * period, 1, 1, 0);

However, the first argument of "BASS_SampleCreate" must be specified as an integer, and a value such as 0.04 can not be specified. What should I do?

In addition, how should I specify the number of elements in the creation part of the following array?
Whether to round up cycles or round off, which is better?

Code: [Select]
short *data = new short[cycles * period]; // data buffer

Best regards.

Ian @ un4seen

  • Administrator
  • Posts: 21991
"seconds" and "frequency" can be floating-point but "cycles" should be left as an integer.

riku

  • Posts: 17
Hello.

Thank you very much.
I can now get it to work almost as desired.
But there is one problem.
As an example, I have specified 0.04 seconds, but if I do this, "BASS_GetLength" will return seconds slightly shorter than the specified number of seconds like 0.0363636363636364 and 0.0353786622443339.
Is there a way to fix this?

Best regards.

Ian @ un4seen

  • Administrator
  • Posts: 21991
The generated sample data contains a whole number of cycles. Generating an extra fractional cycle may result in a "click" sound at the end. If you would prefer to round the length up (instead of down), you can do this:

Code: [Select]
int cycles = ceil(seconds * frequency); // sample length in cycles

riku

  • Posts: 17
Thank you for the advice.

It became the operation that I wanted.

Best regards.