Author Topic: 1. Save part of a Wav file into a new Wav file // Some more questions  (Read 397 times)

RC

  • Posts: 14
Hi everyone,

1) I'd like o load a wav sample file, .. grab a part of it .. and save with the same settings as new wav file.

Would that work like this?
Pseudo code:
Code: [Select]
HS = BASS_SampleLoad(, file,... BASS_SAMPLE_FLOAT)
BASS_SampleGetInfo(HS, info)

DWORD fpflag = 0
if (info.origres == 32)
        fpflag = BASS_ENCODE_FP_32BIT
if (info.origres == 24)
fpflag = BASS_ENCODE_FP_24BIT
if (info.origres == 16)
fpflag = BASS_ENCODE_FP_16BIT
if (info.origres == 8)
fpflag = BASS_ENCODE_FP_8BIT

float orgBuffer[info.length]
BASS_SampleGetData(HS, orgBuffer)

float newBuffer[1000] = orgBuffer[500-1500] // copy data

HSTREAM stream = BASS_StreamCreateFile(false, filename, 0, 0, BASS_STREAM_DECODE | BASS_SAMPLE_FLOAT)
BASS_StreamPutData(stream, newBuffer, newBuffer.length)


But what I have to do then?

2) Does a simple stereo wav have 2 channels, or does it have one channel? If it has one channel, how the data are structured per LEFT, RIGHT on BASS_SAMPLE_FLOAT?
Like this ?
float[0] = left
float[1] = right
float[2] = left
float[3] = right
etc..

3) I didn't find the struct HCHANNEL in the documentation.
channels = (HCHANNEL*)malloc(info.max * sizeof(HCHANNEL)

4) Why BASS didn't have a BASS_SampleWrite(shandle) function?

Thank you!
« Last Edit: 8 Jul '21 - 01:33 by RC »

QuentinC

  • Posts: 70
IF you want to write some part of a file in another file, in fact you don't need samples.
Just create a normal decoding channel:

Code: [Select]
stream = BASS_StreamCreateFile("input.wav", FALSE, 0, 0, BASS_STREAM_DECODE);

From there you can use the bassenc addon to save some part of your input file.
You can also use BASS_ChannelGetData if you want to get the data directly.

Quote
2) Does a simple stereo wav have 2 channels, or does it have one channel? If it has one channel, how the data are structured per LEFT, RIGHT on BASS_SAMPLE_FLOAT?

No, there is only a single HCHANNEL to manipulate, regardless or the number of audio channels in the file.
Data are normally interlaced as you say.

Quote
3) I didn't find the struct HCHANNEL in the documentation.

That's normal, the implementation is private inside the BASS Dlibrary. Take it as a void*.
(Note that it isn't a pointer, though)

Quote
4) Why BASS didn't have a BASS_SampleWrite(shandle) function?

Yann will confirm, but probably because samples may be loaded in hardware, and the operation may not be possible directly.
Additionally, BASS without addons is basically unable to save audio files. That's the role of the optional bassenc addon.

Ian @ un4seen

  • Administrator
  • Posts: 23892
It isn't possible to set an encoder on a sample (HSAMPLE/HCHANNEL), so you should create a stream (HSTREAM) instead, ie. use BASS_StreamCreateFile instead of BASS_SampleLoad. You should also use the BASS_STREAM_DECODE flag when doing that to create a "decoding channel", which can then be processed as quickly as possible with BASS_ChannelGetData. You can use BASS_ChannelSetPosition to set the start and end positions for the encoding. For example, something like this:

Code: [Select]
HSTREAM decoder = BASS_StreamCreateFile(false, infile, 0, 0, BASS_STREAM_DECODE | BASS_SAMPLE_FLOAT); // create decoder for input file

BASS_CHANNELINFO info;
BASS_ChannelGetInfo(decoder, &info); // get format info
DWORD fpflag = 0;
if (!(info.origres & BASS_ORIGRES_FLOAT)) { // set floating-point conversion...
if (info.origres > 24) fpflag = BASS_ENCODE_FP_32BIT
else if (info.origres > 16) fpflag = BASS_ENCODE_FP_24BIT
else if (info.origres > 8) fpflag = BASS_ENCODE_FP_16BIT
else if (info.origres) fpflag = BASS_ENCODE_FP_8BIT
}
BASS_Encode_Start(decoder, outfile, BASS_ENCODE_PCM | BASS_ENCODE_AUTOFREE | fpflag, NULL, NULL); // set a WAV writer on the decoder

BASS_ChannelSetPosition(decoder, BASS_ChannelSeconds2Bytes(decoder, startpos), BASS_POS_BYTE); // seek to start position (in seconds)
BASS_ChannelSetPosition(decoder, BASS_ChannelSeconds2Bytes(decoder, endpos), BASS_POS_END); // set end position (in seconds)

// processing loop
for (;;) {
BYTE buffer[20000]; // processing buffer
int c = BASS_ChannelGetData(decoder, buffer, sizeof(buffer)); // get data from decoder (and send to encoder)
if (c < 0) break; // done
}

BASS_StreamFree(decoder); // free decoder (and encoder due to AUTOFREE)

Please see the documentation for details on the mentioned functions.

RC

  • Posts: 14
IF you want to write some part of a file in another file, in fact you don't need samples.

Thanks a lot for your answer!

3) If stereo is a interleaved single channel:
How do I find out if it's a mono or stereo file then?


It isn't possible to set an encoder on a sample (HSAMPLE/HCHANNEL), so you should create a stream (HSTREAM) instead, ie. use BASS_StreamCreateFile instead of BASS_SampleLoad. You should also use the BASS_STREAM_DECODE flag when doing that to create a "decoding channel", which can then be processed as quickly as possible with BASS_ChannelGetData. You can use BASS_ChannelSetPosition to set the start and end positions for the encoding. For example, something like this:

Thanks a lot and also for the example code!

I think i have it so far.. I'd like to run through the sample data of the infile to calculate posStart and posEnd before decode.
At first I must get the length of the sample data to define myReadBuffer to calculate through.
Is this correct?

Code: [Select]
int lenBytes = BASS_ChannelGetLength(decoder, BASS_POS_BYTE);

float myReadBuffer[lenBytes / sizeof( "float" )]

int ret = BASS_ChannelGetData(decoder, myReadBuffer, BASS_DATA_FLOAT);

// ... read through samples and find slice points as byte positions

// ... so its enough to use:
BASS_ChannelSetPosition(decoder, startpos, BASS_POS_BYTE); // seek to start position (in bytes)
BASS_ChannelSetPosition(decoder, endpos, BASS_POS_END); // seek to start position (in bytes)

...

Thank you!



Ian @ un4seen

  • Administrator
  • Posts: 23892
How do I find out if it's a mono or stereo file then?

You can check that with BASS_ChannelGetInfo (see the "chans" value).

I think i have it so far.. I'd like to run through the sample data of the infile to calculate posStart and posEnd before decode.
At first I must get the length of the sample data to define myReadBuffer to calculate through.
Is this correct?

Code: [Select]
int lenBytes = BASS_ChannelGetLength(decoder, BASS_POS_BYTE);

float myReadBuffer[lenBytes / sizeof( "float" )]

int ret = BASS_ChannelGetData(decoder, myReadBuffer, BASS_DATA_FLOAT);

// ... read through samples and find slice points as byte positions

// ... so its enough to use:
BASS_ChannelSetPosition(decoder, startpos, BASS_POS_BYTE); // seek to start position (in bytes)
BASS_ChannelSetPosition(decoder, endpos, BASS_POS_END); // seek to start position (in bytes)

...

Yes, that looks good. The BASS_DATA_FLOAT flag is unnecessary because the data will already be floating-point due to the BASS_SAMPLE_FLOAT flag in the BASS_StreamCreateFile call.

RC

  • Posts: 14
Thanks again.

---

Found a small error in the documentation
BASS_ChannelGetInfo:
https://www.un4seen.com/doc/#bass/BASS_ChannelGetInfo.html

Filling info.flags
https://www.un4seen.com/doc/#bass/BASS_CHANNELINFO.html

A combination of these flags.
...
BASS_SAMPLE_8BITS   The channel's resolution is 8-bit. If neither this or the BASS_SAMPLE_FLOAT flags are present, then the channel's resolution is 16-bit.
BASS_SAMPLE_FLOAT   The channel's resolution is 32-bit floating-point.
BASS_SAMPLE_LOOP   The channel is looped.
...

BASS_SAMPLE_FLOAT is not bit=2, it's 256 = bit 8



David_AVD

  • Posts: 48
Where did you see that BASS_SAMPLE_FLOAT = 2 ?

The Bass header files define it as 256 so if you use the constant name it should be correct.

RC

  • Posts: 14
In https://www.un4seen.com/doc/#bass/BASS_CHANNELINFO.html

Thought about this list of flags is sorted by bit numbers. But seems not.