Author Topic: Create file single frequency  (Read 635 times)

Ed1966

  • Posts: 248
Create file single frequency
« on: 27 Sep '23 - 06:33 »
Hi There,

I don't know where to begin so with a little help?

I want to create a test file with a tone at a certain frequency with a length of 30 seconds.

Example:
5000Hz
0dB
30 seconds
44100Hz / 16bit

.WAV is fine

Regards,
Eduard.

Ed1966

  • Posts: 248
Re: Create file single frequency
« Reply #1 on: 27 Sep '23 - 17:32 »
Maybe this is what I need?
I have found something and I hear sound but I want to write it to disc.
This is not working: BASS_Encode_Start

Code: [Select]
procedure TForm1.Button1Click(Sender: TObject);
var
  ch: HCHANNEL;
  sample: HSAMPLE;
  data: array[0..128] of SHORT;
var
  I: Integer;
begin
  // Example
  // Create a 440 Hz sine wave sample.

  sample := BASS_SampleCreate(256, 440 * 64, 1, 1, BASS_SAMPLE_LOOP or BASS_SAMPLE_OVER_POS); // create sample
  Error('BASS_SampleCreate' + BASS_ErrorGetCode().ToString); //OK

  for I := 0 to 128 do
    data[I] := Round(32767.0 * sin(I * 6.283185 / 64)); // sine wave

  BASS_SampleSetData(sample, @data); // set the sample's data
  Error('BASS_SampleSetData' + BASS_ErrorGetCode().ToString); // OK

  // Get a sample channel
  ch := BASS_SampleGetChannel(sample, 0);

  // Start writing a channel to a WAV file (output.wav).
  BASS_Encode_Start(ch, 'd:\output.wav', BASS_ENCODE_PCM, nil, nil);
  Error('BASS_Encode_Start' + BASS_ErrorGetCode().ToString); // ERROR

  BASS_ChannelPlay(ch, true); // start the channel playing & encoding
  Error('BASS_ChannelPlay' + BASS_ErrorGetCode().ToString); // OK
end;

Chris

  • Posts: 2217
Re: Create file single frequency
« Reply #2 on: 27 Sep '23 - 17:56 »
To set an encoder on a channel the channel must be have the flag BASS_STREAM_DECODE
so
ch := BASS_SampleGetChannel(sample, 0); --> ch := BASS_SampleGetChannel(sample, BASS_STREAM_DECODE or BASS_SAMCHAN_STREAM);

Chris

  • Posts: 2217
Re: Create file single frequency
« Reply #3 on: 27 Sep '23 - 17:58 »
To set an encoder on a channel the channel must be have the flag BASS_STREAM_DECODE and will need the BASS_SAMCHAN_STREAM flag.
so
ch := BASS_SampleGetChannel(sample, 0); --> ch := BASS_SampleGetChannel(sample, BASS_STREAM_DECODE or BASS_SAMCHAN_STREAM);

Ed1966

  • Posts: 248
Re: Create file single frequency
« Reply #4 on: 28 Sep '23 - 03:26 »
Thank you Chris.
Unfortunately it doesn't work yet and I get the same error at BASS_Encode_Start.

Code: [Select]
procedure TForm1.Button1Click(Sender: TObject);
var
  Enc: HENCODE;
  stream: HSTREAM;  //ch: HCHANNEL;
  sample: HSAMPLE;
  data: array[0..128] of SHORT;
var
  I: Integer;
begin
  // Example
  // Create a 440 Hz sine wave sample.

  sample := BASS_SampleCreate(256, 440 * 64, 1, 1, BASS_SAMPLE_LOOP or BASS_SAMPLE_OVER_POS); // create sample
  Error('BASS_SampleCreate' + BASS_ErrorGetCode().ToString); //OK

  for I := 0 to 128 do
    data[I] := Round(32767.0 * sin(I * 6.283185 / 64)); // sine wave

  BASS_SampleSetData(sample, @data); // set the sample's data
  Error('BASS_SampleSetData' + BASS_ErrorGetCode().ToString); // OK

  // Get a sample channel
  stream := BASS_SampleGetChannel(sample, BASS_STREAM_DECODE or BASS_SAMCHAN_STREAM);
  Error('BASS_SampleGetChannel' + BASS_ErrorGetCode().ToString); // OK

  // Start writing a channel to a WAV file (output.wav).
  Enc := BASS_Encode_Start(stream, 'output.wav', BASS_ENCODE_PCM, nil, nil);
  Error('BASS_Encode_Start' + BASS_ErrorGetCode().ToString); // ERROR
  // ErrorCode = 5 (BASS_ERROR_HANDLE)

  BASS_ChannelPlay(stream, true); // start the channel playing & encoding
  Error('BASS_ChannelPlay' + BASS_ErrorGetCode().ToString); // OK
end;

Regards,
Eduard.

Ian @ un4seen

  • Administrator
  • Posts: 26172
Re: Create file single frequency
« Reply #5 on: 28 Sep '23 - 16:05 »
BASS_ERROR_HANDLE indicates that the "stream" handle in your BASS_Encode_Start call is invalid. What is its value? Please also check the BASS version with BASS_GetVersion. The BASS_SAMCHAN_STREAM option was introduced in BASS 2.4.16.

Ed1966

  • Posts: 248
Re: Create file single frequency
« Reply #6 on: 29 Sep '23 - 08:32 »
Thanks Ian,
This was a version problem.

But now there is no sound and de output is not written.
I see only file 'o.wav' with 0 bytes.

Without 'BASS_STREAM_DECODE' there is sound but also 0 bytes file

For me sound is not directly important but output file is.

Code: [Select]
  // Get a sample channel
  stream := BASS_SampleGetChannel(sample, BASS_STREAM_DECODE or BASS_SAMCHAN_STREAM);
  Error('BASS_SampleGetChannel' + BASS_ErrorGetCode().ToString); // OK

  // Start writing a channel to a WAV file (output.wav).
  BASS_Encode_Start(stream, 'output.wav', BASS_ENCODE_PCM, nil, nil);
  Error('BASS_Encode_Start' + BASS_ErrorGetCode().ToString); // OK
  // Output file = 'o.wav'. 0 Bytes.

  BASS_ChannelPlay(stream, True); // start the channel playing & encoding
  Error('BASS_ChannelPlay' + BASS_ErrorGetCode().ToString); // OK
  // ErrorCode 38

Regards,
Eduard.

Ian @ un4seen

  • Administrator
  • Posts: 26172
Re: Create file single frequency
« Reply #7 on: 29 Sep '23 - 12:29 »
If you want to play the stream while you encode it then you should indeed remove the BASS_STREAM_DECODE flag from the BASS_SampleGetChannel call.

If you're getting a "o.wav" file when asking for an "output.wav" file, that's strange. Perhaps you meant just "o"? If so, adding the BASS_UNICODE flag to your BASS_Encode_Start call should fix that. It looks like you're using Delphi, which I believe usually has Unicode strings.

Also make sure you call BASS_Encode_Stop before closing your app to properly finalize the file's header.

Ed1966

  • Posts: 248
Re: Create file single frequency
« Reply #8 on: 1 Oct '23 - 16:20 »
I have it working now, but how can I make a 10Khz test signal with this? And then volume at highest. 0dB or what I want.
And so also 11Khz, 12Khz etc.
Or is 'BASS_SampleCreate' this not the right command for what I want?
Thanks in advance.

Ian @ un4seen

  • Administrator
  • Posts: 26172
Re: Create file single frequency
« Reply #9 on: 2 Oct '23 - 17:49 »
I would use BASS_StreamCreate rather than BASS_SampleCreate. For example, something like this:

Code: [Select]
float sinstep, sinpos, sinlevel;
DWORD sinremain;

DWORD CALLBACK SinStreamProc(HSTREAM handle, void *buffer, DWORD length, void *user)
{
float *fbuf = (float*)buffer;
if (length > sinremain) length = sinremain;
for (DWORD a = 0; a < length / sizeof(float); a++) {
fbuf[a] = sinlevel * sin(sinpos);
sinpos += sinstep;
}
sinremain -= length;
if (!sinremain) return length | BASS_STREAMPROC_END;
return length;
}

...

sinstream = BASS_StreamCreate(44100, 1, BASS_SAMPLE_FLOAT, SinStreamProc, 0);
sinstep = 2 * M_PI * sinfreq / 44100;
sinpos = 0;
sinlevel = pow10(sindb / 20);
sinremain = BASS_ChannelSeconds2Bytes(stream, sintime);
BASS_ChannelPlay(sinstream, false);

Set sinfreq (frequency), sindb (dB level) and sintime (length in seconds) as wanted.

Ed1966

  • Posts: 248
Re: Create file single frequency
« Reply #10 on: 3 Oct '23 - 03:27 »
Thank you, Ian.

I'll try converting it to Delphi and see what the result is.

Ed1966

  • Posts: 248
Re: Create file single frequency
« Reply #11 on: 4 Oct '23 - 09:58 »
Hi!

I'm stuck on this function. How can I write to buffer (pointer)
what is meant by: // float *fbuf = (float*) buffer; ??
and how to do in Delphi.

Code: [Select]
function SinStreamProc(handle: HSTREAM; buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall;
var
  a: integer;
  fbuf: array of FLOAT;
begin

  // float *fbuf = (float*)buffer; ??

  SetLength(fbuf, length);
  fbuf := (buffer); // ???????

  if (length > sinremain) then
    length := sinremain;
  for a := 0 to Pred(length div SizeOf(FLOAT)) do
  begin
     fbuf[a] := sinlevel * sin(sinpos); // ???
     sinpos := sinpos + sinstep;
  end;
  sinremain := sinremain - length;
  if (sinremain < 0) then
    result := BASS_STREAMPROC_END
  else
    result := length;
end;

Regards,
Eduard.

Chris

  • Posts: 2217
Re: Create file single frequency
« Reply #12 on: 4 Oct '23 - 10:19 »
Hi Ed
to casting a Array to a Pointer is in Delphi a StaticArray needed (and not a dynamically)
so its recommend something like this.

Code: [Select]
var
  stream: HSTREAM; // ch: HCHANNEL;
  sinstep, sinpos, sinlevel: single;

  sinremain: DWORD;

function SinStreamProc(FStream: HSTREAM; buffer: Pointer; length: DWORD; user: Pointer): DWORD; stdcall;
type
  TBufArray = array [0 .. (maxInt div sizeof(single)) - 1] of single;
  PBufArray = ^TBufArray;
var
  fBuf: PBufArray;
  a: integer;
begin
  fBuf := Pointer(buffer);
  if (length > sinremain) then
    length := sinremain;
  for a := 0 to (length div sizeof(single)) - 1 do
  begin
    fBuf[a] := sinlevel * sin(sinpos);
    inc(sinpos,sinstep);
  end;
  dec(sinremain, length);
  if (sinremain = DW_Error) then // -1
    result := length or BASS_STREAMPROC_END
  else
    result := length;
end;



« Last Edit: 4 Oct '23 - 16:11 by Chris »

Ed1966

  • Posts: 248
Re: Create file single frequency
« Reply #13 on: 4 Oct '23 - 18:06 »
Thank You!  ;)

SoundMike

  • Posts: 369
Re: Create file single frequency
« Reply #14 on: 6 Oct '23 - 02:15 »
Interesting! I might be able to adapt this code to provide a 'pilot tone'. A 'pilot tone' needs to keep playing continuously, so I would need to add some code to loop the channel.

Ian @ un4seen

  • Administrator
  • Posts: 26172
Re: Create file single frequency
« Reply #15 on: 6 Oct '23 - 12:39 »
You can make the stream never-ending by removing the "sinremain" part. If it'll be going for a long time, you should also prevent "sinpos" getting too high and losing precision. Like this:

Code: [Select]
DWORD CALLBACK SinStreamProc(HSTREAM handle, void *buffer, DWORD length, void *user)
{
float *fbuf = (float*)buffer;
for (DWORD a = 0; a < length / sizeof(float); a++) {
fbuf[a] = sinlevel * sin(sinpos);
sinpos += sinstep;
}
sinpos = fmod(sinpos, 2 * M_PI); // prevent sinpos losing precision
return length;
}

SoundMike

  • Posts: 369
Re: Create file single frequency
« Reply #16 on: 6 Oct '23 - 23:07 »
Thanks, Ian. When I get time I'll try this.