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

MikeV

  • Posts: 155
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: 20424
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: 155
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: 155
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: 20424
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: 155
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: 155
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: 155
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: 155
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: 20424
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: 155
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.