Author Topic: BASS_WASAPI and CastInit  (Read 2931 times)

Ed1966

  • Posts: 51
BASS_WASAPI and CastInit
« on: 3 Feb '17 - 09:21 »
Hi :)

I want to stream Soundcard output to the ShoutCast webserver but there is a problem with: BASS_Encode_Start
I now only hear garbage noise and don't know if this snippet is correct. First time to use this.
Will you take a look and give me the right solution?

Code: [Select]
function WasapiProc(buffer:Pointer; length:DWORD; user:Pointer): DWORD; stdcall;
begin
  Result := BASS_ChannelGetData(Channel, buffer, length);
end;

begin
    BASS_WASAPI_Init(-3, 0, 0, 0, 0.5, 0, @WasapiProc, nil);
    BASS_WASAPI_GetInfo(&info); // get sample format info

    Channel := BASS_StreamCreate(info.freq, info.chans, BASS_STREAM_DECODE, STREAMPROC_DUMMY, 0);

    { Only Garbage, noise?? }
    BASS_Encode_Stop(Channel);
    Encoder := BASS_Encode_Start(Channel, 'lame -r -s 44100 -b %d -', [128], BASS_ENCODE_NOHEAD or BASS_ENCODE_AUTOFREE, nil, 0);
    BASS_Encode_CastInit(Encoder, PAnsiChar(FullServer), PAnsiChar(FullLogin), BASS_ENCODE_TYPE_MP3, 'Name', 'Url', 'Genre', nil, nil, 128, True);

    { Title is send correctly and visible server site }
    BASS_Encode_CastSetTitle(Encoder, PAnsiChar(FullTitle), nil);

    Bool := BASS_WASAPI_Start();
end;

I have tried this piece of code for BASS_Encode_Start but there is no sound.

Code: [Select]
  BASS_Encode_Stop(Channel);
  Encoder := BASS_Encode_Start(Channel, nil, BASS_ENCODE_PCM or BASS_ENCODE_NOHEAD or BASS_ENCODE_AUTOFREE, nil, 0);

Hope you see what I am doing wrong.
Regards,
Eduard.
« Last Edit: 3 Feb '17 - 09:36 by Ed1966 »

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #1 on: 3 Feb '17 - 16:18 »
Your WASAPIPROC function (WasapiProc) will be receiving floating-point data, so the "Channel" stream should also be floating-point; add BASS_SAMPLE_FLOAT to the BASS_StreamCreate call. Also add BASS_ENCODE_FP_16BIT to the BASS_Encode_Start call because LAME doesn't support floating-point data. You should also use the WASAPI device's "info.freq" value in the LAME command-line (it might not be 44100).

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #2 on: 4 Feb '17 - 06:23 »
I have added flags and modify Lame (I use latest version) and there is less garbage and sometimes I hear a voice (I play test mp3 song).
(Voice is unrecognizable and slow. Sound is very low)

Problem is not solved at this moment. Have you got more tips? 
Here is de full new code:

Code: [Select]
begin
  BASS_Free;
  BASS_Init(0, 44100, 0, Handle, nil);

  BASS_WASAPI_Init(-3, 0, 0, 0, 0.5, 0, @WasapiProc, nil);

  BASS_WASAPI_GetInfo(&info); // Result: Freq. 48000, 8 Channels

  Channel := BASS_StreamCreate(info.freq, info.chans, BASS_SAMPLE_FLOAT or BASS_STREAM_DECODE, STREAMPROC_DUMMY, 0);

  BASS_Encode_Stop(Channel);
  LameCommand := Format(AddBS(EncoderPath) + 'lame -r -s %d -b %d -', [info.freq, 256]);
  Comline := PChar(AnsiString(LameCommand));
  Encoder := BASS_Encode_Start(Channel, ComLine, BASS_ENCODE_FP_16BIT or BASS_ENCODE_NOHEAD or BASS_ENCODE_AUTOFREE, nil, 0);

  BASS_Encode_CastInit(Encoder, PAnsiChar(FullServer), PAnsiChar(FullLogin), BASS_ENCODE_TYPE_MP3, 'Name', 'Url', 'Genre', nil, nil, 128, True);

  BASS_Encode_CastSetTitle(Encoder, PAnsiChar(FullTitle), nil);

  BASS_WASAPI_Start();
end;

Or is it possible to put Encoder in MIXER and sent Mixer to CastInit?
Kind regards.
Eduard.
« Last Edit: 5 Feb '17 - 11:39 by Ed1966 »

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #3 on: 6 Feb '17 - 17:34 »
Code: [Select]
begin
  BASS_WASAPI_GetInfo(&info); // Result: Freq. 48000, 8 Channels

If the sample data has 8 channels, that will be a problem because MP3 doesn't support more than stereo. You will need to downmix it to stereo or just use the first 2 channels. A mixer (eg. BASS_Mixer_StreamCreate) could be used to implement either of those solutions. A splitter (eg. BASS_Split_StreamCreate) is also an option for the latter. In either case, you would need to change the dummy stream ("Channel") to a push stream, which you can plug into a mixer or splitter. Using a mixer could look something like this:

Code: [Select]
inputstream = BASS_StreamCreate(info.freq, info.chans, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE, STREAMPROC_PUSH, 0); // create push stream to hold WASAPI data
mixer = BASS_Mixer_StreamCreate(info.freq, 2, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE); // create stereo mixer
BASS_Mixer_StreamAddChannel(mixer, inputstream, 0); // plug input stream into mixer (add BASS_MIXER_DOWNMIX for downmixing)
BASS_Encode_Start(mixer, ...); // set encoder on mixer

...

DWORD CALLBACK WasapiProc(void *buffer, DWORD length, void *user)
{
BASS_StreamPutData(inputstream, buffer, length); // pass data to push stream
while (1) {
BYTE buf[20000]; // processing buffer
int r = BASS_ChannelGetData(mixer, buf, sizeof(buf)); // process the mixer (inc. send to encoder)
if (r <= 0) break; // done
}
}

Please see the documentation for details on the aforementioned functions.

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #4 on: 7 Feb '17 - 08:31 »
Thanks Ian. Works great. 

Question: is there a possibility monitor and broadcast sound from my own Application only? 

Regards,
Eduard.

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #5 on: 7 Feb '17 - 13:43 »
I don't believe WASAPI has that option. What you could do is have your app send all playback through a mixer (eg. use BASS_Mixer_StreamAddChannel instead of BASS_ChannelPlay), and set the encoder on that.

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #6 on: 8 Feb '17 - 09:34 »
Hi :)

I know how to put Streams into Mixer like this:
Code: [Select]
Mixer := BASS_Mixer_StreamCreate(44100, 2, BASS_UNICODE);
StreamLeft := BASS_StreamCreateFile(False, PChar(MusicLeft), 0, 0, BASS_STREAM_DECODE or BASS_UNICODE);
StreamRight := BASS_StreamCreateFile(False, PChar(MusicRight), 0, 0, BASS_STREAM_DECODE or BASS_UNICODE);
BASS_Mixer_StreamAddChannel(Mixer, StreamLeft, BASS_MIXER_BUFFER);
BASS_Mixer_StreamAddChannel(Mixer, StreamRight, BASS_MIXER_BUFFER);
BASS_ChannelPlay(Mixer, False);

But I want to use StreamLeft and StreamRight at full control because this is in my DJ program. With on both Streams PLAY, PAUSE, STOP and Progress for scroll position. But I don't know how to do that.

I was thinking of BASS_ChannelPlay(StreamLeft) but that is not working.

Do you have little code for me so I can do some experimental on this?

At first I was thinking about this piece of code but hear no sound. And maybe this is nog the right way.
Code: [Select]
FStream := BASS_StreamCreateFile(False, PChar(MusicFile), 0, 0, BASS_STREAM_DECODE or BASS_UNICODE);
While (Play_Pressed) do begin
  BASS_ChannelGetData(FStream, @FFTDataStream, BASS_DATA_FFT2048);
end;

Regards,
Eduard.

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #7 on: 8 Feb '17 - 17:01 »
I know how to put Streams into Mixer like this:
Code: [Select]
Mixer := BASS_Mixer_StreamCreate(44100, 2, BASS_UNICODE);
StreamLeft := BASS_StreamCreateFile(False, PChar(MusicLeft), 0, 0, BASS_STREAM_DECODE or BASS_UNICODE);
StreamRight := BASS_StreamCreateFile(False, PChar(MusicRight), 0, 0, BASS_STREAM_DECODE or BASS_UNICODE);
BASS_Mixer_StreamAddChannel(Mixer, StreamLeft, BASS_MIXER_BUFFER);
BASS_Mixer_StreamAddChannel(Mixer, StreamRight, BASS_MIXER_BUFFER);
BASS_ChannelPlay(Mixer, False);

But I want to use StreamLeft and StreamRight at full control because this is in my DJ program. With on both Streams PLAY, PAUSE, STOP and Progress for scroll position. But I don't know how to do that.

I was thinking of BASS_ChannelPlay(StreamLeft) but that is not working.

Do you have little code for me so I can do some experimental on this?

Do you mean you want to play/pause/stop the 2 sources individually? If so, you can do that by setting/removing the BASS_MIXER_PAUSE flag on each of them via BASS_Mixer_ChannelFlags:

Code: [Select]
BASS_Mixer_ChannelFlags(channel, BASS_MIXER_PAUSE, BASS_MIXER_PAUSE); // pause

...

BASS_Mixer_ChannelFlags(channel, 0, BASS_MIXER_PAUSE); // unpause

Note that there will be some extra latency added by the mixer due to buffering, ie. pausing/resuming won't be heard instantly. You can reduce the latency by reducing the mixer's playback buffer via the BASS_CONFIG_BUFFER option. If minimal latency is required, you could use WASAPI to play the mixer instead (call BASS_ChannelGetData on the mixer in your WASAPIPROC function).

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #8 on: 9 Feb '17 - 02:04 »
Think I now understand better. Add BASS_Mixer_ChannelSetPosition for position and BASS_Mixer_ChannelRemove to remove when loading new song.
This must than be right preparing left Player?
Code: [Select]
BASS_Mixer_ChannelRemove(channel);
BASS_Mixer_StreamAddChannel(mixer, channel, BASS_MIXER_BUFFER);
BASS_Mixer_ChannelFlags(channel, BASS_MIXER_PAUSE, BASS_MIXER_PAUSE); // pause
BASS_Mixer_ChannelSetPosition(channel, 0, BASS_POS_BYTE);
And always use BASS_ChannelPlay(mixer, False) or must this be True? 

Quote
If minimal latency is required, you could use WASAPI to play the mixer instead (call BASS_ChannelGetData on the mixer in your WASAPIPROC function).

What do you mean by that. I don't think I understand.

Regards,
Eduard.

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #9 on: 9 Feb '17 - 14:20 »
Think I now understand better. Add BASS_Mixer_ChannelSetPosition for position and BASS_Mixer_ChannelRemove to remove when loading new song.
This must than be right preparing left Player?
Code: [Select]
BASS_Mixer_ChannelRemove(channel);
BASS_Mixer_StreamAddChannel(mixer, channel, BASS_MIXER_BUFFER);
BASS_Mixer_ChannelFlags(channel, BASS_MIXER_PAUSE, BASS_MIXER_PAUSE); // pause
BASS_Mixer_ChannelSetPosition(channel, 0, BASS_POS_BYTE);
And always use BASS_ChannelPlay(mixer, False) or must this be True? 

You would usually use restart=FALSE in BASS_ChannelPlay calls, but you can use restart=TRUE if you want to clear the mixer's playback buffer, eg. perhaps after seeking.

If you want to add a source to a mixer in a paused state, it would be best to include the BASS_MIXER_PAUSE flag in the BASS_Mixer_StreamAddChannel call instead of calling BASS_Mixer_ChannelFlags afterwards.

Quote
If minimal latency is required, you could use WASAPI to play the mixer instead (call BASS_ChannelGetData on the mixer in your WASAPIPROC function).

What do you mean by that. I don't think I understand.

I was just saying that if you want low latency, then it would probably be better to play the mixer via WASAPI instead of using normal BASS playback. If you would like to try that, you can take a look at the CONTEST.C example code that's included in the BASSWASAPI package for a little demonstration.

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #10 on: 10 Feb '17 - 03:05 »
Thank you for your effort and time. :)

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #11 on: 13 Feb '17 - 18:02 »
Hi, Maybe you can help me again?

BASS_ENCODE_TYPE_MP3 and 'Lame' is working fine.

Now I want to add OGG and AAC.

This is what I have created for 'BASS_ENCODE_TYPE_OGG':
Code: [Select]
LAME_COMMAND := Format('oggenc2 -r -M %d -m %d -R %d -C %d -', [SetCastBitRate, SetCastBitRate, WASAPI_INFO.freq, WASAPI_INFO.chans]); // BASS_ENCODE_TYPE_OGG
  Comline := PChar(AnsiString(LAME_COMMAND));
  GlobalEncoder := BASS_Encode_Start(OutputMixer, ComLine,
    BASS_ENCODE_FP_16BIT or BASS_ENCODE_NOHEAD or BASS_ENCODE_CAST_NOLIMIT, nil, nil);

And this one for 'BASS_ENCODE_TYPE_AAC':
Code: [Select]
LAME_COMMAND := Format('enc_aacplus - - --br 128000 --he --silent --rawpcm %d %d 16', [WASAPI_INFO.freq, WASAPI_INFO.chans]); // BASS_ENCODE_TYPE_AAC
  Comline := PChar(AnsiString(LAME_COMMAND));
  GlobalEncoder := BASS_Encode_Start(OutputMixer, ComLine,
    BASS_ENCODE_FP_16BIT or BASS_ENCODE_NOHEAD or BASS_ENCODE_CAST_NOLIMIT, nil, nil);

There is no sound. Can  you see what I am doing wrong with LAME_COMMAND?
I try a few but no results after hours searching forum. The 'BASS_Encode_CastInit' had the right BASS_ENCODE_TYPE.  ???

Regards,
Eduard.

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #12 on: 14 Feb '17 - 15:58 »
Is the BASS_Encode_Start call reporting success in its return value, ie. "GlobalEncoder" is not 0? If so, what does your BASS_Encode_CastInit call look like, and is that also reporting success?

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #13 on: 15 Feb '17 - 05:29 »
This is my log.

Code: [Select]
15-02-2017 05:59:53 ~ Broadcast: EncoderCommand: oggenc2 -r -M 128 -m 128 -R 48000 -C 2 -
15-02-2017 05:59:53 ~ Broadcast: EncoderContent: application/ogg
15-02-2017 05:59:53 ~ Broadcast: BASS_Encode_Start (1784)
15-02-2017 05:59:53 ~ Broadcast: BASS_Encode_CastInit (True)
15-02-2017 05:59:53 ~ Broadcast: BASS_WASAPI_Start (True)
15-02-2017 05:59:53 ~ Broadcast: BASS_Encode_CastSetTitle (True)

Code: [Select]
15-02-2017 06:03:08 ~ Broadcast: EncoderCommand: enc_aacplus - - --br 128000 --he --silent --rawpcm 48000 2 16
15-02-2017 06:03:08 ~ Broadcast: EncoderContent: audio/aacp
15-02-2017 06:03:08 ~ Broadcast: BASS_Encode_Start (1628)
15-02-2017 06:03:08 ~ Broadcast: BASS_Encode_CastInit (True)
15-02-2017 06:03:08 ~ Broadcast: BASS_WASAPI_Start (True)
15-02-2017 06:03:08 ~ Broadcast: BASS_Encode_CastSetTitle (False) [Some other mystery error]

OGG connects and see Title broadcasting but no sound.
AAC nothing visible on screen server side.

Do you think Encoder Command is correct?

Is it possible that streaming service (I received guest account to test my software) is not support OGG and/or AAC ??

I notice ACC+ ? Is that the same as AAC?

Regards,
Eduard.

Chris

  • Posts: 1808
Re: BASS_WASAPI and CastInit
« Reply #14 on: 15 Feb '17 - 09:02 »
Code: [Select]
-M 128 -m 128

-M Specify a maximum bitrate in kbps. Useful for streaming applications.
-m Specify a minimum bitrate (in kbps). Useful for  encoding for a fixed-size channel.

 while
-b Choose a nominal bitrate to encode at. Attempt
                      to encode at a bitrate averaging this. Takes an
                      argument in kbps. By default, this produces a VBR
                      encoding, equivalent to using -q or --quality.

so try -b 128

About AAC AAC+
thats are 2 different Encoders
AAC  Low Quality  Encoder
aacPlus v2  High Quality  Encoder

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #15 on: 15 Feb '17 - 15:36 »
Is it possible that streaming service (I received guest account to test my software) is not support OGG and/or AAC ??

What software is the streaming service running? I notice you mentioned Shoutcast in the 1st post. Shoutcast doesn't supports OGG, but it should support AAC. Have you tried using the pre-compliled CAST.EXE example that's included in the BASSenc package (C\BIN folder) to connect to the server? If not, you could give that a try and see what results you get.

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #16 on: 16 Feb '17 - 04:18 »
I have tried CAST.EXE but could not test it with AAC.

Thanks Ian (and Chris) for help and information. I will do some tests this weekend.

Regards,
Eduard.

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #17 on: 16 Feb '17 - 09:27 »
To continued my journey for Broadcasting. Here is another question. The microphone.
See code:

Code: [Select]
function StartMicroPhone: Boolean;
begin
  { Initialize BASS WASAPI input }
  Result := ExBASS_WASAPI_Init(-2, 0, 0, 0, 0.0, 0, @DuffRecording, nil);
  MicroSetDevice := ExBASS_WASAPI_GetDevice();

  { Create a stream to receive the data }
  Result := ExBASS_WASAPI_GetInfo(&WASAPI_INFO);
  MicroInputStream := ExBASS_StreamCreate(WASAPI_INFO.freq, WASAPI_INFO.chans, BASS_SAMPLE_FLOAT or BASS_STREAM_DECODE, STREAMPROC_PUSH, nil);

  { Initialize default output device (and measure latency) }
  Result := ExBASS_WASAPI_Init(-1, 0, 0, 0, 0.4, 0.05, @OutputRecording, nil);

  MicroOutputDevice := ExBASS_WASAPI_GetDevice();
  Result := ExBASS_WASAPI_GetInfo(&WASAPI_INFO);

  // Create a mixer to feed the output device
  MicroOutputMixer := ExBASS_Mixer_StreamCreate(WASAPI_INFO.freq, WASAPI_INFO.chans, BASS_SAMPLE_FLOAT or BASS_STREAM_DECODE);
  Result := ExBASS_Mixer_StreamAddChannel(MicroOutputMixer, MicroInputStream, 0); // plug in the input stream

  ExBASS_WASAPI_SetDevice(MicroSetDevice); // set device context to input
  ExBASS_WASAPI_Start(); // start it

  ExBASS_WASAPI_SetDevice(MicroOutputDevice); // set device context to output
  ExBASS_WASAPI_Start(); // start it
end;


Sound is good but there is a small delay. Is there any improvement to minimize delay? Or is this the best way.
I don't have tested RecordStart but look at forum there is more delay. Correct? 

Regards,
Eduard.

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #18 on: 16 Feb '17 - 14:36 »
The delay there is mostly determined by the output buffer (rather than the input/recording buffer). So for lower latency, you will need to reduce the size of that. You could try event-driven buffering, which will automatically use a smallish buffer in shared mode (usually 30ms), like this:

Code: [Select]
  Result := ExBASS_WASAPI_Init(-1, 0, 0, BASS_WASAPI_EVENT, 0, 0, @OutputRecording, nil);

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #19 on: 17 Feb '17 - 05:48 »
Nice. Thank you.

Maybe you can add:  BASS_WASAPI_EVENT = 16; in Dynamic_WASAPI.pas because it's missing.

Regards,
Eduard.


Chris

  • Posts: 1808
Re: BASS_WASAPI and CastInit
« Reply #20 on: 17 Feb '17 - 09:39 »
If you are working with the "dynamic bass* Units" be shure to compare them with the "static bass* Units" because the Dynamic Units are from 2011.
« Last Edit: 17 Feb '17 - 13:17 by Chris »

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #21 on: 18 Feb '17 - 10:35 »
Yes, Chris I known about this. But I thought there was some update if needed.
All your help make me create a better program and thanks for this.

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #22 on: 23 Feb '17 - 19:06 »
Now everything is working fine (MP3, OGG (IceCast) , AAC). I stream it from my free Jukebox.
Question became here if it is possible to broadcast to MP3 and AAC at the same time? (two different Servers)
I think use for multi : BASS_Encode_CastInit ?
What can I do? Object or is there a way to do this in same code?

Ian @ un4seen

  • Administrator
  • Posts: 20336
Re: BASS_WASAPI and CastInit
« Reply #23 on: 24 Feb '17 - 12:35 »
To broadcast to 2 servers, you can call BASS_Encode_Start and BASS_Encode_CastInit twice, once for each server.

Ed1966

  • Posts: 51
Re: BASS_WASAPI and CastInit
« Reply #24 on: 25 Feb '17 - 07:26 »
And can I use the same OutputMixer? (Look back for source)

Encoder1 := BASS_Encode_Start(OutputMixer, ...
BASS_Encode_CastInit(Encoder1, ...

Encoder2 := BASS_Encode_Start(OutputMixer, ...
BASS_Encode_CastInit(Encoder2, ..

Must do some rewriting so must know for sure.

Regards,
Eduard.