21 May '13 - 12:11 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: BassMix: Mute channels till ChannelSetPosition() in a loop  (Read 650 times)
SLancer
Posts: 5


« on: 16 Mar '12 - 12:05 »
Reply with quoteQuote

Hi Everyone,

found this great libs/great forum a few days ago and starting to rewrite some code based on another lib  Wink
for I see much more options going with BASS etc. .
(I'm on Win7-64, VS2010, C#)

After reading the manuals (BASS, BASSMix) and learning from from your code BASS_ChannelPlay(..) runs well.
Now switching to BASSMix and want to know some basics.

So how is it possible to hear streams in the loop first and not during initialization ?
(It's part of a proggi playing SingleGuitarNotes, SingleKeyboardNotes, Drums etc.)

streamMixer = BassMix.BASS_Mixer_StreamCreate(44100,2,BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_SAMPLE_SOFTWARE | BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_BUFFER | BASSFlag.BASS_MIXER_NONSTOP);

bytArr1 = File.ReadAllBytes("Wave1.wav");
bytArr2 = File.ReadAllBytes("Wave2wav");

_hGCFile1 = GCHandle.Alloc(bytArr1,GCHandleType.Pinned);
_hGCFile2 = GCHandle.Alloc(bytArr2,GCHandleType.Pinned);

iStreams[0] = Bass.BASS_StreamCreateFile(_hGCFile1.AddrOfPinnedObject(),0L,bytArr1.Length,BASSFlag.BASS_STREAM_DECODE |     BASSFlag.BASS_SAMPLE_FLOAT);
iStreams[1] = Bass.BASS_StreamCreateFile(_hGCFile2.AddrOfPinnedObject(),0L,bytArr2.Length,BASSFlag.BASS_STREAM_DECODE |     BASSFlag.BASS_SAMPLE_FLOAT);

Bass.BASS_ChannelPlay(streamMixer,false);

// Adding channels
BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[0],BASSFlag.BASS_STREAM_DECODE |
      BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_PAUSE);
BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],BASSFlag.BASS_STREAM_DECODE |
      BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_PAUSE);

// How to avoid ouput here... ?
BassMix.BASS_Mixer_ChannelFlags(iStreams[0],0,BASSFlag.BASS_MIXER_PAUSE);    // remove BASS_MIXER_PAUSE flag
BassMix.BASS_Mixer_ChannelFlags(iStreams[1],0,BASSFlag.BASS_MIXER_PAUSE);    // remove BASS_MIXER_PAUSE flag

// TestLoop  (for getting the basics)
while(...)
{
      if(...)
      {
          // ...but hear it HERE first !
          // ------------------------
          BassMix.BASS_Mixer_ChannelSetPosition(iStreams[0],0);
          Bass.BASS_ChannelPause(iStreams[1]); // virtual note-off
      }
      else
      {
          BassMix.BASS_Mixer_ChannelSetPosition(iStreams[1],0);
          Bass.BASS_ChannelPause(iStreams[0]); // virtual note-off
      }
      ..
      ..
}

I tried it with BASS_ChannelLock, PAUSE-flags, replacing/adding flag, SetPosition() to position streams to the end first,
but where is the trick to have the first output IN the loop for the mixer is playing already ?
(I don't wanna use Callbacks at this time)

Thanks for your help !
« Last Edit: 16 Mar '12 - 12:22 by SLancer » Logged
Ian @ un4seen
Administrator
Posts: 15253


« Reply #1 on: 16 Mar '12 - 15:13 »
Reply with quoteQuote

I'm not sure I fully understand what you want to do, but I do spot a few issues in the code...

streamMixer = BassMix.BASS_Mixer_StreamCreate(44100,2,BASSFlag.BASS_MIXER_PAUSE | BASSFlag.BASS_SAMPLE_SOFTWARE | BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_BUFFER | BASSFlag.BASS_MIXER_NONSTOP);

...

BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[0],BASSFlag.BASS_STREAM_DECODE |
      BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_PAUSE);
BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],BASSFlag.BASS_STREAM_DECODE |
      BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_MIXER_PAUSE);

Some of these flags aren't valid for the functions, so they could potentially be doing things that you don't want, ie. if their values coincide with other flags that are used by the functions. BASS_Mixer_StreamCreate doesn't use BASS_MIXER_PAUSE or BASS_MIXER_BUFFER, and BASS_Mixer_StreamAddChannel doesn't use BASS_STREAM_DECODE or BASS_SAMPLE_FLOAT. Please see the documentation for a list of the flags that are supported by each function.

// How to avoid ouput here... ?
BassMix.BASS_Mixer_ChannelFlags(iStreams[0],0,BASSFlag.BASS_MIXER_PAUSE);    // remove BASS_MIXER_PAUSE flag
BassMix.BASS_Mixer_ChannelFlags(iStreams[1],0,BASSFlag.BASS_MIXER_PAUSE);    // remove BASS_MIXER_PAUSE flag

Removing the BASS_MIXER_PAUSE flag will cause the mixer to resume/start playing the source. Is that what you want to avoid? If so, remove those BASS_Mixer_ChannelFlags calls Smiley

      if(...)
      {
          // ...but hear it HERE first !
          // ------------------------
          BassMix.BASS_Mixer_ChannelSetPosition(iStreams[0],0);
          Bass.BASS_ChannelPause(iStreams[1]); // virtual note-off
      }
      else
      {
          BassMix.BASS_Mixer_ChannelSetPosition(iStreams[1],0);
          Bass.BASS_ChannelPause(iStreams[0]); // virtual note-off
      }

BASS_ChannelPause only applies to normal playback, eg. when BASS_ChannelPlay is used. To pause a mixer source, you can use BASS_Mixer_ChannelFlags to set the BASS_MIXER_PAUSE flag instead...

BassMix.BASS_Mixer_ChannelFlags(iStreams[1],BASSFlag.BASS_MIXER_PAUSE,BASSFlag.BASS_MIXER_PAUSE);    // set BASS_MIXER_PAUSE flag
Logged
SLancer
Posts: 5


« Reply #2 on: 16 Mar '12 - 16:39 »
Reply with quoteQuote

Hi Ian,

thanks for your quick reply !

My current code is placed around a Stopwatch-Class (.NET Framework 4) where samples (single notes)
are played in different loops/threats.

So I like to replace the current audioEngine with BASS/BASSMix etc. and switch to the Callbacks later.
But I want to "control" the dynamically added iStreams[..] with one Mixer for effects etc. .

Corrected the flags as you mentioned (looks much better.. Wink) and looks like:

streamMixer = BassMix.BASS_Mixer_StreamCreate(44100,2,BASSFlag.BASS_SAMPLE_SOFTWARE |  ASSFlag.BASS_SAMPLE_FLOAT |
    BASSFlag.BASS_MIXER_NONSTOP);

bytArr1 = File.ReadAllBytes(sPathMetronome + "Note1.wav");
bytArr2 = File.ReadAllBytes(sPathMetronome + "Note2.wav");

_hGCFile1 = GCHandle.Alloc(bytArr1,GCHandleType.Pinned);
_hGCFile2 = GCHandle.Alloc(bytArr2,GCHandleType.Pinned);

iStreams[0] = Bass.BASS_StreamCreateFile(_hGCFile1.AddrOfPinnedObject(),0L,bytArr1.Length,BASSFlag.BASS_STREAM_DECODE |
    BASSFlag.BASS_SAMPLE_FLOAT);

iStreams[1] = Bass.BASS_StreamCreateFile(_hGCFile2.AddrOfPinnedObject(),0L,bytArr2.Length,BASSFlag.BASS_STREAM_DECODE |
    BASSFlag.BASS_SAMPLE_FLOAT);

Bass.BASS_ChannelPlay(streamMixer,false);

// How to avoid ouput here... ? (Seems not)
(*) BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[0],0);
(*) BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],0);

// TestLoop  (for getting the basics)
while(...)
{
      if(...)
      {
          // ...but hear it HERE first !
          // ------------------------
(**)    BassMix.BASS_Mixer_ChannelSetPosition(iStreams[0],0);
          ..          
      }
      else
      {
          BassMix.BASS_Mixer_ChannelSetPosition(iStreams[1],0);
          ..
      }
      ..
      ..
}

Understanding while the mixer is playing, AddChannels or SetPostion(0) produce an output.

So how to get the FIRST output on (**) and not on (*) as I don't want to have any output before the loop (**) ?
(Adding a flag ?, hehehehe...)

Maybe adding/removing channels at runtime inside the loop ?

Thanks for your help !
« Last Edit: 16 Mar '12 - 16:46 by SLancer » Logged
Ian @ un4seen
Administrator
Posts: 15253


« Reply #3 on: 16 Mar '12 - 17:31 »
Reply with quoteQuote

OK. That looks like you could indeed add/remove the sources within the loop, something like this...

     if(...)
      {
          BassMix.BASS_Mixer_ChannelRemove(iStreams[1]); // remove stream 1
          Bass.BASS_ChannelSetPosition(iStreams[0],0); // cue stream 0
          BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[0],0); // add it
      }
      else
      {
          BassMix.BASS_Mixer_ChannelRemove(iStreams[0]); // remove stream 0
          Bass.BASS_ChannelSetPosition(iStreams[1],0); // cue stream 1
          BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],0); // add it
      }

When you switch to using syncs, you would move that code (or something similar) to your SYNCPROC callback function.
« Last Edit: 20 Mar '12 - 14:57 by Ian @ un4seen » Logged
SLancer
Posts: 5


« Reply #4 on: 16 Mar '12 - 19:06 »
Reply with quoteQuote

I commented out this part not to hear anything:
Bass.BASS_ChannelPlay(streamMixer,false);
// (*) BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[0],0);
// (*) BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],0);

and modificated:
while(...)
{
      if(...)
      {
          // You don't mean:
          // -----BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],0); // remove stream 1
          // but ?
          BassMix.BASS_Mixer_ChannelRemove(iStreams[1]);     // remove stream 1
          Bass.BASS_ChannelSetPosition(iStreams[0],0);       // cue stream 0
          BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[0],0);   // add it
      }
      else
      {
          // same as above
          // -----BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[0],0); // remove stream 0
          BassMix.BASS_Mixer_ChannelRemove(iStreams[0]);     // remove stream 0
          Bass.BASS_ChannelSetPosition(iStreams[1],0);       // cue stream 1
          BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],0);  // add it
      }
}


adding a bit of time prior to the loop for this simple code:
System.Threading.Thread.Sleep(100);

Otherwise I noticed a little latency only right after the 1st note was played.
But normally initialization is separated, certainly.

The two line BASS_Mixer_ChannelRemove(..) results in a false for only the first 2 calls
for I commentd out the two lines BASS_Mixer_StreamAddChannel() before the loop, hm..
OK, they are just flags.  Wink

Last Q: Where is the reference to the BASSMixer (streamMixer) in ?:
BassMix.BASS_Mixer_ChannelRemove(iStreams[1]);    // remove stream 1

Seems, only 1 mixer is allowed or source will be removed from all mixers.

It's running now solid with 1000 Bpm and not like a drunken horse like before.  Wink
(Yes, yes, the flags...)

I will move it to the SYNCPROC callback function later on.

Thank you Ian !!
« Last Edit: 19 Mar '12 - 18:57 by SLancer » Logged
Ian @ un4seen
Administrator
Posts: 15253


« Reply #5 on: 19 Mar '12 - 15:10 »
Reply with quoteQuote

          // You don't mean:
          // -----BassMix.BASS_Mixer_StreamAddChannel(streamMixer,iStreams[1],0); // remove stream 1
          // but ?
          BassMix.BASS_Mixer_ChannelRemove(iStreams[1]);     // remove stream 1

Oops, I did indeed mean BASS_Mixer_ChannelRemove. Too much copy'n'pasting Smiley

adding a bit of time prior to the loop for this simple code:
System.Threading.Thread.Sleep(100);

Otherwise I noticed a little latency only right after the 1st note was played.

That will be related to the mixer's playback buffer level, ie. it will be empty to begin with and will eventually fill up (to the BASS_CONFIG_BUFFER setting), at which point the latency should be fairly consistent. Your Sleep call will give the buffer some time to fill up (with silence due to the BASS_MIXER_NONSTOP flag) first.

Note you won't need to bother with that when using syncs to trigger things; they will be triggered at the sync positions.

The two line BASS_Mixer_ChannelRemove(..) results in a false for only the first 2 calls
for I commentd out the two lines BASS_Mixer_StreamAddChannel() before the loop, hm..

Yes, that is to be expected as the sources haven't been added to the mixer yet at that point. It isn't a problem, ie. the failed calls won't do any harm.

Last Q: Where is the reference to the BASSMixer (streamMixer) in ?:
BassMix.BASS_Mixer_ChannelRemove(iStreams[1]);    // remove stream 1

Seems, only 1 mixer is allowed or source will be removed from all mixers.

It isn't possible for multiple mixers to share a source because they would be taking data away from each other, ie. the data that one mixer gets from the source won't be available to the other mixers. So a source can only be added to one mixer at a time, which also means that there is no need for a mixer handle to be provided in BASS_Mixer_ChannelXXX function calls.

If you happen to want to play a source in multiple mixers simultaneously, you will need to either create duplicates (eg. additional BASS_StreamCreateFile calls) or use BASSmix's splitter stream functionality (eg. BASS_Split_StreamCreate). Note the latter is only applicable when the mixers are playing the same content from the source, eg. you couldn't have one mixer playing the start of a file and another playing the end (the source can't be at 2 places at the same time).
Logged
SLancer
Posts: 5


« Reply #6 on: 19 Mar '12 - 18:32 »
Reply with quoteQuote

Quote
Oops, I did indeed mean BASS_Mixer_ChannelRemove. Too much copy'n'pasting

But that's nearly the only way ro avoid mistakes while coding..  Wink

Thanks for the other infos around BASSMix !

Quote
It isn't possible for multiple mixers to share a source because they would be taking data away from each other, ie. the data that one mixer gets from the source won't be available to the other mixers. So a source can only be added to one mixer at a time, which also means that there is no need for a mixer handle to be provided in BASS_Mixer_ChannelXXX function calls.

If you happen to want to play a source in multiple mixers simultaneously, you will need to either create duplicates (eg. additional BASS_StreamCreateFile calls) or use BASSmix's splitter stream functionality (eg. BASS_Split_StreamCreate). Note the latter is only applicable when the mixers are playing the same content from the source, eg. you couldn't have one mixer playing the start of a file and another playing the end (the source can't be at 2 places at the same time).

No, I don't want to share identical sources with multiple mixers.

The consideration was to have different mixers in different objects.
So I just rename the sources and it will work.

Quote
Note you won't need to bother with that when using syncs to trigger things; they will be triggered at the sync positions.

That will be the next step playing around with syncs/Triggers as you mentioned to check out your realtime scheduler.
Hope it will make things easier like SpeedChange, TimeStretching and so forth to replace my own timeline.

Would be nice to have a new BASSMix-Flag during init that prevent the Mixer from "playing" the whole time to have it as a "real" mixer.
Means, it should  monitor the streams anytime, but should only play/stop a source by 1-line-command:
(Maybe there is one..)

BassMix.BASS_Mixer_ChannelPlay(mixer,source,bool);

So you could add channels, set positions whenever you want and only play/stop it with one command above.

Or even prevent other sources from playing WHILE playing:

BassMix.BASS_Mixer_ChannelPlay(mixer, source, bool play/stop, bool playExclusive );

Could be much easier to handle the BASSMixer.

Another short Q:
To fill the buffer after initialization (BASS) I use the 2 lines as a workaround:
1) Bass.BASS_ChannelPlay(iStreams[0],false);
2) Bass.BASS_ChannelPause(iStreams[0]);

Without these 2 lines the first note in a sequence has a latence once, otherwise timing is OK.

Tried to code it with BASS_CONFIG_BUFFER, but did not get the same result as with the 2 lines.

Would be nice to get a short example in C#.

--
Time to avoid this annoying splash-screen shortly.
Hope my girlfriend is in good mood. Wink

Thanks again Ian !
« Last Edit: 20 Mar '12 - 13:18 by SLancer » Logged
Ian @ un4seen
Administrator
Posts: 15253


« Reply #7 on: 20 Mar '12 - 14:53 »
Reply with quoteQuote

Would be nice to have a new BASSMix-Flag during init that prevent the Mixer from "playing" the whole time...

That can be done with the BASS_MIXER_PAUSE flag, ie. set the flag to stop the source and unset the flag to play it...

BASS_Mixer_StreamAddChannel(mixer, source, BASS_MIXER_PAUSE); // add the source to the mixer in a paused state
...
BASS_Mixer_ChannelFlags(source, 0, BASS_MIXER_PAUSE); // play the source (unset the PAUSE flag)
...
BASS_Mixer_ChannelFlags(source, BASS_MIXER_PAUSE, BASS_MIXER_PAUSE); // pause the source (set the PAUSE flag)

To fill the buffer after initialization (BASS) I use the 2 lines as a workaround:
1) Bass.BASS_ChannelPlay(iStreams[0],false);
2) Bass.BASS_ChannelPause(iStreams[0]);

Without these 2 lines the first note in a sequence has a latence once, otherwise timing is OK.

Tried to code it with BASS_CONFIG_BUFFER, but did not get the same result as with the 2 lines.

Those 2 functions don't actually do anything on a decoding channel, and will fail with a BASS_ERROR_DECODE error. Are you sure they made a difference, and it wasn't something else that you're doing? Perhaps it was simply the act of (very slightly) delaying whatever follows that made the difference, and any other calls would have the same effect.

Regarding the BASS_CONFIG_BUFFER option, note that changes to it only affect subsequently created channels (exising channels are unaffected), so it would need to be set before creating the mixer (BASS_Mixer_StreamCreate).
Logged
SLancer
Posts: 5


« Reply #8 on: 20 Mar '12 - 15:44 »
Reply with quoteQuote

Hi Ian,

Quote
That can be done with the BASS_MIXER_PAUSE flag, ie. set the flag to stop the source and unset the flag to play it...
BASS_Mixer_StreamAddChannel(mixer, source, BASS_MIXER_PAUSE); // add the source to the mixer in a paused state
...
BASS_Mixer_ChannelFlags(source, 0, BASS_MIXER_PAUSE); // play the source (unset the PAUSE flag)
...
BASS_Mixer_ChannelFlags(source, BASS_MIXER_PAUSE, BASS_MIXER_PAUSE); // pause the source (set the PAUSE flag)

Thx, I'll try that.

Suggestion:
BASS_Mixer_ChannelFlags(source, 0, BASS_MIXER_PAUSE,*BASS_MIXER_CHANNEL_SOLO*); // plays ONLY THAT source while stopping other sources automatically.

This overload would avoid extra effort with counters which source was played last, mute it with the flag and play the new one = in ONE command.
Very useful when playing shorter samples in a sequence and not to have a "sounds-puree".

It's only a littel function, but would be faster/more comfortable to have it in the DLLs.

Thanks in advance !  Smiley
---

1) Bass.BASS_ChannelPlay(iStreams[0],false);
2) Bass.BASS_ChannelPause(iStreams[0]);
Quote
Those 2 functions don't actually do anything on a decoding channel, and will fail with a BASS_ERROR_DECODE error. Are you sure they made a difference, and it wasn't something else that you're doing? Perhaps it was simply the act of (very slightly) delaying whatever follows that made the difference, and any other calls would have the same effect.

Yes, I tested it quite a few session:

Run program.

1a) Initialization of BASS, some Bass.BASS_StreamCreateFile()s, -------, play the sources (StartButton) => latency 1st note,
      keep playing, StopButton.
1b) Start playing again (everthing is initialized already) => NO latency 1st note

Restart program.

2a) Initialization of BASS, some Bass.BASS_StreamCreateFile()s, ADDED the 2 lines before, play the sources (StartButton) => >>>NO Latency 1st note<<<,
      keep playing, StopButton.
2b) Start playing again (everthing is initialized already) => NO latency 1st note

Maybe the 2 lines preload a few bits into the buffer after all ?  Wink

Quote
Regarding the BASS_CONFIG_BUFFER option, note that changes to it only affect subsequently created channels (exising channels are unaffected), so it would need to be set before creating the mixer (BASS_Mixer_StreamCreate).

OK, I'll play around with it and see what happens.

Maybe I'll recode all routines when switching to the syncprogs...
But this is for the basics first.

Thanks again Ian !
« Last Edit: 21 Mar '12 - 06:04 by SLancer » Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines