Author Topic: Get copy of played audio  (Read 147 times)

MikeV

  • Posts: 158
Get copy of played audio
« on: 6 Dec '17 - 12:39 »
I have a simple mp3 player that creates a stream with BASS_StreamCreateFile() and plays it through an output device set by BASS_Init(). I now need access to the (floating point) audio data that is played as I need a copy of it to be processed elsewhere (non BASS code). I need the copy whether the the stream is actually playing through an output device or not (0 = no sound). What would be the best way to achieve this?

Ian @ un4seen

  • Administrator
  • Posts: 20433
Re: Get copy of played audio
« Reply #1 on: 6 Dec '17 - 14:37 »
You can use a DSP function (DSPPROC) for that, applied via BASS_ChannelSetDSP. It will receive all sample data that the stream produces.

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #2 on: 7 Dec '17 - 14:20 »
Thank you. That sounds like a nice and clean solution, but I just found out my code does not work if I select the "no sound" device. So looks like it's going to be a little less elegant as apparently this only works with a decoding channel.

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #3 on: 7 Dec '17 - 15:27 »
As usual I got completely lost again in BASS :(.. I have experience using decoding channels for input, but not for output.

I've setup StreamCreateFile (1) to decode the sample data. I've used StreamCreate to create a stream that calls MyStreamProc which pulls data from (1). But how do I write the data to the audio device? BASS_Init does not provide me with anything I can use.. Bought a licence almost 7 years ago and still struggle with the logic of it all. It would help tremendously if you would add some more examples to the documentation, i.e. in StreamCreateFile add a second example how to stream a file using a callback.

Thanks.

Ian @ un4seen

  • Administrator
  • Posts: 20433
Re: Get copy of played audio
« Reply #4 on: 7 Dec '17 - 15:42 »
The data will get to the output device by playing the stream that you created with BASS_StreamCreate, ie. calling BASS_ChannelPlay on it. From the "how do I write the data to the audio device" question, it sounds like you may want to push the data rather than have it pulled? If so, you can do that by using proc=STREAMPROC_PUSH in the BASS_StreamCreate call and then calling BASS_StreamPutData to push the data.

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #5 on: 7 Dec '17 - 15:48 »
I figured I'd need a callback so I can create the copy (see my first post)? But I think I get it now, let me give it another try.

Thanks.

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #6 on: 7 Dec '17 - 16:59 »
It's playing now (and I can copy data in the callback, yay), and with some trial & error figured out that to get the position I need to get it from the stream, but to get the overall length I need to get it from the file. However I am unable to set the position, tried both and nothing happens. What did I miss?

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #7 on: 8 Dec '17 - 07:16 »
The copied data format varies with the file, but needs to be a fixed 48 kHz stereo. Odd as it sounds, this requires a mixer, right?

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #8 on: 8 Dec '17 - 12:43 »
Sometimes BASS makes live so simple, sometimes so unnecessarily complicated.. Turns out that your suggestion to use BASS_StreamCreate is not helping as that *also* won't play to the "no sound" device. So it's back to square 1 as I completely ran out of ideas (and getting frustrated that after two days I still did finish this code). I'll try to formulate more precisely this time, knowing the various pitfalls..

I need a seekable mp3 player that plays both to an output device as well as "no sound", and I need access to the played data in floating point 48 kHz stereo samples.

Thanks.

Ian @ un4seen

  • Administrator
  • Posts: 20433
Re: Get copy of played audio
« Reply #9 on: 8 Dec '17 - 13:40 »
The copied data format varies with the file, but needs to be a fixed 48 kHz stereo. Odd as it sounds, this requires a mixer, right?

Yes, you can use a mixer to resample the data received by the DSP function. You would use a "push" stream to feed the data to the mixer. Something like this:

Code: [Select]
resampler = BASS_Mixer_StreamCreate(48000, 2, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE); // create a mixer with wanted sample format
source = BASS_StreamCreate(origfreq, origchans, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE, STREAMPROC_PUSH, 0); // create a push stream with original sample format
BASS_Mixer_StreamAddChannel(resampler, source, BASS_MIXER_NORAMPIN); // plug it into the mixer

...

void CALLBACK DSPProc(HDSP handle, DWORD channel, void *buffer, DWORD length, void *user)
{
BASS_StreamPutData(source, buffer, length); // pass data to push stream
while (1) {
BYTE buf[20000];
int r = BASS_ChannelGetData(resampler, buf, sizeof(buf)); // get resampled data from mixer
if (r <= 0) break; // done
// do something with data in "buf" here
}
}

I need a seekable mp3 player that plays both to an output device as well as "no sound"...

You can simulate playback of a decoding channel by calling BASS_ChannelGetData periodically at real-time speed, eg. in a worker thread or timer.

Code: [Select]
// start "playback"
starttime = timeGetTime();
processed = 0;

...

// process the decoder (in worker thread or timer)
int time = timeGetTime() - starttime; // time since "playback" started
QWORD target = BASS_ChannelSeconds2Bytes(decoder, time/1000.0); // translate to bytes
while (processed < target) {
BYTE buf[20000];
int r = BASS_ChannelGetData(decoder, buf, sizeof(buf)); // decode some data
if (r <= 0) break;
processed += r;
}

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #10 on: 8 Dec '17 - 13:54 »
Wow, that is rather complex for such a basic job. I'm also worried that seeking, which doesn't work now I moved to a decoding stream (as I reported earlier, but you never responded to) will certainly not work when I write it like this.

It's at times like this I have serious doubts whether going with BASS was the right choice. This should be a matter of a few simple calls. E.g. if you added some code so that initialising "no sound" starts an internal clock no further steps were necessary, let alone having to create a thread just to read an mp3. It would behave as a dummy sound card, which is exactly what I would expect.

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #11 on: 13 Dec '17 - 08:41 »
A week ago I asked "However I am unable to set the position, tried both and nothing happens. What did I miss?".

In other words, why does BASS_ChannelSetPosition not work on a decoding channel and, as I have no other options, how do I change the position then?

Ian @ un4seen

  • Administrator
  • Posts: 20433
Re: Get copy of played audio
« Reply #12 on: 13 Dec '17 - 14:58 »
What type of stream are you calling BASS_ChannelSetPosition on, ie. how was it created? BASS_ChannelSetPosition does work on file based "decoding channels", ie. those created by BASS_StreamCreateFile. It doesn't work on streams created by BASS_StreamCreate, as BASS has no knowledge of the stream's data source. An exception is when using pos=0 in the call to clear the stream's playback buffer, which you would do after seeking in the data source, to have the stream immediately play fresh data from the new position.

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #13 on: 13 Dec '17 - 15:44 »
I had it on StreamCreateFile and it no longer worked when I had to change it into a decode stream.

Ian @ un4seen

  • Administrator
  • Posts: 20433
Re: Get copy of played audio
« Reply #14 on: 13 Dec '17 - 16:31 »
That's strange. What was the error code (from BASS_ErrorGetCode) generated by the BASS_ChannelSetPosition call?

MikeV

  • Posts: 158
Re: Get copy of played audio
« Reply #15 on: 13 Dec '17 - 19:31 »
I knew I should have checked as you would ask for it :). As it took to long I used a different library, but will check when/if I get back to this code.