Author Topic: [Linux/C++] Most efficient way of playing streams  (Read 105 times)

gosucherry

  • Posts: 28
Hello!
I would like to ask what is most efficient way of playing streams (plugged into mixer). Im working on c++ linux version of the library. Is there noticeable difference between "BASS_StreamCreateFile" using a file and memory location, should I preload all samples to byte arrays? Im playing lot of streams simultaneously ( lot like hundreds ). And I'm noticing serious underruns. Im trying to optimize stuff, and those underruns are important issue. While using Core 2 Duo e8400 with 4-8gb of ddr2 memory and SSD. Issue occurs while playing back ~170-200mb worth .wav samples, which is way below expectations...  "Renicing" a process with -20 priority doesnt affect anything. Nothing fancy is going on StreamProc. Debug and Release version of my program have pretty much the same performance. Any ideas on that? Thanks in advance, and have nice day!

Ian @ un4seen

  • Administrator
  • Posts: 21208
Re: [Linux/C++] Most efficient way of playing streams
« Reply #1 on: 17 Oct '18 - 15:07 »
If the files will all fit in memory then loading and playing them from there would prevent I/O delays but there's probably not that much difference if the files are on an SSD drive. Are you playing all of the files simultaneously through a single mixer? If so, the mixer could be the performance bottleneck. What do you see if you monitor the mixer's BASS_ATTRIB_CPU value via BASS_ChannelGetAttribute, and also the BASS_GetCPU return value?

gosucherry

  • Posts: 28
Re: [Linux/C++] Most efficient way of playing streams
« Reply #2 on: 17 Oct '18 - 15:29 »
If the files will all fit in memory then loading and playing them from there would prevent I/O delays but there's probably not that much difference if the files are on an SSD drive. Are you playing all of the files simultaneously through a single mixer? If so, the mixer could be the performance bottleneck. What do you see if you monitor the mixer's BASS_ATTRIB_CPU value via BASS_ChannelGetAttribute, and also the BASS_GetCPU return value?

Yes, currently all streams are plugged into single mixer (which is decode channel, splitted later in sub-channels for each of speaker output). Results are:

Code: [Select]
-===IDLE ( 0 streams playing )===-
BASS_ATTRIB_CPU: ~0.90 | BASS_GetCPU: ~3.7

-===LOW LOAD( 26 streams playing )===-
BASS_ATTRIB_CPU: ~15.0 | BASS_GetCPU: ~18.0

-===MID LOAD( 104 streams playing )===-
BASS_ATTRIB_CPU: ~59.0 | BASS_GetCPU: ~63.0

-===HIGH LOAD( 156 streams playing )===-
BASS_ATTRIB_CPU: ~88.0 | BASS_GetCPU: ~93.0

-===OVERWHELMING LOAD( 208 streams playing )===-
BASS_ATTRIB_CPU: ~121.0 | BASS_GetCPU: ~101.0

Ian @ un4seen

  • Administrator
  • Posts: 21208
Re: [Linux/C++] Most efficient way of playing streams
« Reply #3 on: 17 Oct '18 - 16:09 »
That confirms that the mixer is struggling to process the high number of sources quickly enough. Do the mixer and sources all have the same sample rate?

gosucherry

  • Posts: 28
Re: [Linux/C++] Most efficient way of playing streams
« Reply #4 on: 17 Oct '18 - 16:14 »
That confirms that the mixer is struggling to process the high number of sources quickly enough. Do the mixer and sources all have the same sample rate?

Yes, they operate at 48000Hz.

Ian @ un4seen

  • Administrator
  • Posts: 21208
Re: [Linux/C++] Most efficient way of playing streams
« Reply #5 on: 17 Oct '18 - 16:51 »
If there is no resampling involved then I would expect performance to be better than that. Perhaps there is decoding involved; what file format(s) are you playing? I checked just now on a comparable CPU (Athlon X2 250) and a mixer used only 14% CPU to play 200 WAV sources (all with same sample rate) on that.

Another thing that could raise CPU usage is processing small amounts of data at a time (so that overheads become a larger proportion of CPU usage). As it's a decoding channel, are you calling BASS_ChannelGetData on the mixer, and if so, how much data are you requesting each time?

gosucherry

  • Posts: 28
Re: [Linux/C++] Most efficient way of playing streams
« Reply #6 on: 17 Oct '18 - 17:04 »
If there is no resampling involved then I would expect performance to be better than that. Perhaps there is decoding involved; what file format(s) are you playing? I checked just now on a comparable CPU (Athlon X2 250) and a mixer used only 14% CPU to play 200 WAV sources (all with same sample rate) on that.

Another thing that could raise CPU usage is processing small amounts of data at a time (so that overheads become a larger proportion of CPU usage). As it's a decoding channel, are you calling BASS_ChannelGetData on the mixer, and if so, how much data are you requesting each time?

Im playing typical .wav files. Im not calling BASS_ChannelGetData on mixer, because it's specific channels are used for "BASS_Split_StreamCreate". However im using StreamProc for my "playHandle". To make things alittle more clear :

Creating streams :

Code: [Select]
loopHandle = BASS_StreamCreateFile(false,waveFile,0,0,BASS_STREAM_DECODE);
playHandle = BASS_StreamCreate(48000,2,BASS_STREAM_DECODE,(STREAMPROC*)&StreamProc,this);
BASS_Mixer_StreamAddChannel(mixer,playHandle,BASS_MIXER_PAUSE);

And my StreamProc is :

Code: [Select]
DWORD CALLBACK StreamProc(HSTREAM handle, BYTE *buffer, DWORD length, void * user){
    int count=length;
    while (count) {
        int r=BASS_ChannelGetData((*(Dzwiek *)user).loopHandle,buffer,count);
        if (r==-1){ // it's ended, go to start of loop
            BASS_ChannelSetPosition((*(Dzwiek *)user).loopHandle,((*(Dzwiek *)user).loopStart*4),BASS_POS_BYTE);
        }
        else {
            buffer+=r;
            count-=r;
        }
    }
    return length;
}

And the "count" value seems to be always 1920.

Ian @ un4seen

  • Administrator
  • Posts: 21208
Re: [Linux/C++] Most efficient way of playing streams
« Reply #7 on: 17 Oct '18 - 17:46 »
1920 bytes is 10ms, assuming 48000 Hz stereo 16-bit data. During normal playback, it should match the BASS_CONFIG_UPDATEPERIOD setting (after pre-buffering), so it looks like you have disabled playback buffering via the BASS_ATTRIB_BUFFER/NOBUFFER option? That would mean the mixer is processed more often in smaller amounts, increasing the CPU usage, as described above. I modified my test to disable playback buffering and that raised the CPU usage to 48%, which is quite a bit higher than before but still below your numbers.

To perhaps help locate where some of the extra CPU usage is coming from, can you try plugging the WAV files directly into the mixer, instead of going through a STREAMPROC?

gosucherry

  • Posts: 28
Re: [Linux/C++] Most efficient way of playing streams
« Reply #8 on: 17 Oct '18 - 18:06 »
1920 bytes is 10ms, assuming 48000 Hz stereo 16-bit data. During normal playback, it should match the BASS_CONFIG_UPDATEPERIOD setting (after pre-buffering), so it looks like you have disabled playback buffering via the BASS_ATTRIB_BUFFER/NOBUFFER option? That would mean the mixer is processed more often in smaller amounts, increasing the CPU usage, as described above. I modified my test to disable playback buffering and that raised the CPU usage to 48%, which is quite a bit higher than before but still below your numbers.

To perhaps help locate where some of the extra CPU usage is coming from, can you try plugging the WAV files directly into the mixer, instead of going through a STREAMPROC?

Yes, im using BASS_ATTRIB_BUFFER with value 0 (because there was some bug with "NOBUFFER" option :>). After plugging wav's directly into the mixer and playing 208 streams, the CPU usage was about ~70%. Much better, but still not so impressive... Any further advices for speeding things up (since job done StreamProc can be achieved by SyncProc :) )?


EDIT: I've tested it on second PC with i5-2410M, and I've achieved similiar results. Plugging in external USB soundcard (Presonus USB Studio) doesnt seem to affect performance at all.
« Last Edit: 17 Oct '18 - 19:09 by gosucherry »

Ian @ un4seen

  • Administrator
  • Posts: 21208
Re: [Linux/C++] Most efficient way of playing streams
« Reply #9 on: 18 Oct '18 - 16:04 »
Here's a BASS update for you to try, which should reduce the overheads a little when there are many 100s of streams:

   www.un4seen.com/stuff/bass-linux.zip

Let me know whether you see any difference in your case.

Regarding disabling playback buffering via BASS_ATTRIB_BUFFER, is there a particular reason that you're doing that? Re-enabling playback buffering (so that the mixer gets processed at longer intervals) should greatly improve performance.

gosucherry

  • Posts: 28
Re: [Linux/C++] Most efficient way of playing streams
« Reply #10 on: 18 Oct '18 - 16:54 »
Here's a BASS update for you to try, which should reduce the overheads a little when there are many 100s of streams:

   www.un4seen.com/stuff/bass-linux.zip

Let me know whether you see any difference in your case.

Regarding disabling playback buffering via BASS_ATTRIB_BUFFER, is there a particular reason that you're doing that? Re-enabling playback buffering (so that the mixer gets processed at longer intervals) should greatly improve performance.

Version you've uploaded reduced the usage by much. I were able to play 312 streams with ~93.0 usage, while it was over 100 with 208 streams :) I disabled playback buffering because my application is piano sampler, and any delays between MIDI IN and Playback must be the shortest possible...

Ian @ un4seen

  • Administrator
  • Posts: 21208
Re: [Linux/C++] Most efficient way of playing streams
« Reply #11 on: 18 Oct '18 - 17:56 »
Version you've uploaded reduced the usage by much. I were able to play 312 streams with ~93.0 usage, while it was over 100 with 208 streams :)

Good to hear that the update helped. It may be possible to tweak that stuff a bit more. I'll look into it :)

I disabled playback buffering because my application is piano sampler, and any delays between MIDI IN and Playback must be the shortest possible...

Have you considered using the BASSMIDI add-on for playback, instead of BASSmix and a bunch of decoders? That should be a lot more efficient. You can define the samples to play in SFZ files, which could be generated in memory at runtime. If you're unfamiliar with the SFZ format, you can find info on it here: www.sfzformat.com

rv

  • Posts: 239
Re: [Linux/C++] Most efficient way of playing streams
« Reply #12 on: 18 Oct '18 - 19:28 »
Can we stream sfz with bassmidi from hard drive? the goal is to not load all the file in memory, but only few parts of each wav

gosucherry

  • Posts: 28
Re: [Linux/C++] Most efficient way of playing streams
« Reply #13 on: 18 Oct '18 - 19:35 »
Version you've uploaded reduced the usage by much. I were able to play 312 streams with ~93.0 usage, while it was over 100 with 208 streams :)

Good to hear that the update helped. It may be possible to tweak that stuff a bit more. I'll look into it :)

I disabled playback buffering because my application is piano sampler, and any delays between MIDI IN and Playback must be the shortest possible...

Have you considered using the BASSMIDI add-on for playback, instead of BASSmix and a bunch of decoders? That should be a lot more efficient. You can define the samples to play in SFZ files, which could be generated in memory at runtime. If you're unfamiliar with the SFZ format, you can find info on it here: www.sfzformat.com

It wouldnt be that easy, since im using FX'es on those channels :)