Author Topic: Mix and play to tracks on two output devices  (Read 370 times)

PandaSharp

  • Guest
Hi, I'm developing a DJ (Windows) app that should support:
  • Playing 2 tracks at same time on the main sound card, with volume and crossfading
  • Playing 1 or 2 tracks (on request) on the secondary sound card, important that the track must be in sync between the 2 devices
So far I decided for the following approach (don't worry for the temporary hardcoded values):
Code: [Select]
// Initialize the 2 devices
Bass.Init(2);
Bass.Init(3);

// Initialize the mixer on the primary device
_mixer = BassMix.CreateMixerStream(44100, 2, 0);
Bass.ChannelSetDevice(_mixer, 2);
Bass.ChannelPlay(_mixer);

// Add the song to the mixer
_source1 = Bass.CreateStream(audioBytes, 0, audioBytes.Length, BassFlags.Decode);
BassMix.MixerAddChannel(_mixer, _source1, 0); // add it to the mix

// Add the song to the second device
var source = Bass.CreateStream(audioBytes, 0, audioBytes.Length, BassFlags.Float);
Bass.ChannelSetDevice(source, 3);
Bass.ChannelPlay(source);

//Set volume
Bass.ChannelSetAttribute(_source1, ChannelAttribute.Volume, value);

//Play/Pause
if (_isPlaying)
{
    BassMix.ChannelFlags(_source1, BassFlags.MixerChanPause, BassFlags.MixerChanPause);
    Bass.ChannelPlay(_source1);
}
else
{
    BassMix.ChannelFlags(_source1, 0, BassFlags.MixerChanPause);
    Bass.ChannelPause(_source1);
}

Anyone one can suggest a better approach? any feedback is welcome!
Thanks

Ian @ un4seen

  • Administrator
  • Posts: 23924
Re: Mix and play to tracks on two output devices
« Reply #1 on: 4 Aug '21 - 14:07 »
If the song will be playing in sync (ie. same position) on both devices then one change you could make is to use splitters to do that, which will usually be more efficient because there's only one decoder then. It could look something like this:

Code: [Select]
split1 = BASS_Split_StreamCreate(source, BASS_STREAM_DECODE, NULL); // create splitter for mixer
BASS_SetDevice(device); // set device for separate playback splitter
split2 = BASS_Split_StreamCreate(source, 0, NULL); // create splitter for separate playback
BASS_Mixer_StreamAddChannel(mixer, split1, 0); // add 1st splitter to the mixer
BASS_ChannelPlay(split2, false); // start playing the 2nd splitter

Please see the BASS_Split_StreamCreate documentation for details on that. It looks like you're using a wrapper but the code hopefully shouldn't be very much different with that.

When a source is to be started in a paused state, I would recommend setting the pause flag in the BASS_Mixer_StreamAddChannel call rather than separately with BASS_Mixer_ChannelFlags, to avoid any chance of the mixer briefly playing the source between those calls. Similarly for the separate stream, skip BASS_ChannelPlay rather than calling BASS_ChannelPlause afterwards.

Note you don't necessarily need a mixer to play multiple songs with crossfading (BASS_ChannelSlideAttribute can be used with BASS_ATTRIB_VOL to crossfade regular playback), although it can helpful for synchronising the songs, eg. starting them at exact points within the mix.

PandaSharp

  • Guest
Re: Mix and play to tracks on two output devices
« Reply #2 on: 4 Aug '21 - 15:08 »
Thanks a lot for your reply, following your suggestion now the code looks better, like this:

Code: [Select]
var source = Bass.CreateStream(audioBytes, 0, audioBytes.Length, BassFlags.Decode); // create decoder for 1st file
 _splitMix1 = BassMix.CreateSplitStream(source, BassFlags.Decode, null); // create splitter for mixer

 _split1 = BassMix.CreateSplitStream(source, 0, null); // create splitter for separate playback
Bass.ChannelSetDevice(_split1, headphonesDevice); // set device for separate playback splitter

BassMix.MixerAddChannel(_mixer, _splitMix1, BassFlags.MixerChanPause); // add 1st splitter to the mixer

only problem to play/pause I still need to do 2 calls, did I do something wrong? because like this I lose the sync

Code: [Select]
if (_isPlaying2)
{
    BassMix.ChannelFlags(_splitMix2, BassFlags.Default, BassFlags.MixerChanPause);
    Bass.ChannelPlay(_split2);
}
else
{
    BassMix.ChannelFlags(_splitMix2, BassFlags.MixerChanPause, BassFlags.MixerChanPause);
    Bass.ChannelPause(_split2);
}

Does the mixer support the output of the same stream on 2 devices at same time? maybe with the matrix?

Meanwhile I'll keep playing with different options and check also this out:
Quote
Note you don't necessarily need a mixer to play multiple songs with crossfading (BASS_ChannelSlideAttribute can be used with BASS_ATTRIB_VOL to crossfade regular playback), although it can helpful for synchronising the songs, eg. starting them at exact points within the mix.

Ian @ un4seen

  • Administrator
  • Posts: 23924
Re: Mix and play to tracks on two output devices
« Reply #3 on: 4 Aug '21 - 16:22 »
Code: [Select]
var source = Bass.CreateStream(audioBytes, 0, audioBytes.Length, BassFlags.Decode); // create decoder for 1st file
 _splitMix1 = BassMix.CreateSplitStream(source, BassFlags.Decode, null); // create splitter for mixer

 _split1 = BassMix.CreateSplitStream(source, 0, null); // create splitter for separate playback
Bass.ChannelSetDevice(_split1, headphonesDevice); // set device for separate playback splitter

BassMix.MixerAddChannel(_mixer, _splitMix1, BassFlags.MixerChanPause); // add 1st splitter to the mixer

only problem to play/pause I still need to do 2 calls, did I do something wrong? because like this I lose the sync

You would indeed need to play/pause both separately. Regarding losing sync, that may be mostly due to the extra buffering involved in mixer playback compared to normal playback. You can try skipping that extra buffering by setting BASS_ATTRIB_BUFFER to 0 on the mixer:

Code: [Select]
BASS_ChannelSetAttribute(mixer, BASS_ATTRIB_BUFFER, 0); // disable playback buffering

Please see the BASS_ATTRIB_BUFFER documentation for details.

Does the mixer support the output of the same stream on 2 devices at same time? maybe with the matrix?

It isn't possible for a mixer itself to output on multiple devices, but you can use splitters to play a mixer on multiple devices. In that case, the BASS_STREAM_DECODE flag would be set on the mixer and not on the splitters (so that the splitters can be played with BASS_ChannelPlay). You could also link the splitters via BASS_ChannelSetLink so that they play/pause together, although that doesn't necessarily guarantee that they'll stay perfectly in sync, particularly when on different devices.

PandaSharp

  • Guest
Re: Mix and play to tracks on two output devices
« Reply #4 on: 4 Aug '21 - 16:57 »
Thanks really a lot for your useful answers, disabling the buffer did the trick, while I'll keep playing with your suggestions trying to figure it out the best solution, I have one last question. Is it possible to play two splitsreams on 2 different devices without a mixer? something like this but doesn't look like works, or most probably I'm doing something wrong:

Quote
var source = Bass.CreateStream(audioBytes, 0, audioBytes.Length, BassFlags.Decode); // create decoder for 1st file

_splitMix1 = BassMix.CreateSplitStream(source, 0, null); // create splitter for mixer
Bass.ChannelSetDevice(_splitMix1, masterDevice); // set device for separate playback splitter

_split1 = BassMix.CreateSplitStream(source, 0, null); // create splitter for separate playback
Bass.ChannelSetDevice(_split1, headphonesDevice); // set device for separate playback splitter

//Bass.ChannelSetLink(_splitMix1, _split1);
Bass.ChannelPlay(source);

If, as it looks, Bass is going to satisfy all my needs, I'll be really happy to do a donation, if you accept them somehow please point me out on how to do it... The app I'm trying to develop will be free (no ads) and opensource.

PandaSharp

  • Guest
Re: Mix and play to tracks on two output devices
« Reply #5 on: 4 Aug '21 - 17:23 »
ok now it works, I think I did some small mistakes during my code changes, this is the code without a mixer, time to play and choose the best option :)

Code: [Select]
var source = Bass.CreateStream(audioBytes, 0, audioBytes.Length, BassFlags.Decode); // create decoder for 1st file

_splitMix1 = BassMix.CreateSplitStream(source, 0, null); // create splitter for mixer
var a  = Bass.ChannelSetDevice(_splitMix1, masterDevice); // set device for separate playback splitter

_split1 = BassMix.CreateSplitStream(source, 0, null); // create splitter for separate playback
var s  = Bass.ChannelSetDevice(_split1, headphonesDevice); // set device for separate playback splitter

Bass.ChannelSetLink(_splitMix1, _split1);
Bass.ChannelPlay(_splitMix1);

PandaSharp

  • Guest
Re: Mix and play to tracks on two output devices
« Reply #6 on: 6 Sep '21 - 16:33 »
Thanks a lot for your help and your library, I released a first version of my app, if anyone would like to try it or collaborate is more than welcome, here there are the links:
download and install > https://www.microsoft.com/store/apps/9PKX15RGL06W
source code > https://github.com/panda-sharp/Yugen.DJ