Author Topic: Capturing complete audio track data with BASS_ChannelGetData  (Read 377 times)

soundgals

  • Posts: 63
Thanks to Ian's help, I'm now able to playback DSD over DoP or convert DSD to PCM for playback. This will be a choice for my app's users.

There is just one more choice I would like to provide, and that is to obtain all data from a DSF/DFF music track converted to PCM, before playback. This is to allow me to apply DSP to that captured data prior to playback.

First I plan to create the stream as follows…

stream = BASS_DSD_StreamCreateURL(theURL, 0, DWORD(BASS_STREAM_DECODE), nil, nil, DWORD(BASS_CONFIG_DSD_FREQ))

For subsequently capturing the data, the only examples I've seen are for C# and VB on this page; http://bass.radio42.com/help/html/a13cfef0-1056-bb94-81c4-a4fdf21bd463.htm

This speaks about a 30ms time window. I haven't a clue as to how to begin translating this to Swift code for capturing all the data converted to PCM of a track, and returning it to the calling function.

Any help will be much appreciated.

Ian @ un4seen

  • Administrator
  • Posts: 25725
You can request up to 268435455 (0xFFFFFFF) bytes of data from a single BASS_ChannelGetData call. If your stream is longer then you can use multiple calls to get it all. Something like this:

Code: [Select]
QWORD length = BASS_ChannelGetLength(stream, BASS_POS_BYTE); // get length
BYTE *data = new BYTE[length]; // allocate memory
QWORD done = 0;
while (done < length) {
int got = BASS_ChannelGetData(stream, data + done, min(length - done, 0xFFFFFFF)); // get data
if (got == -1) { // error/end
length = done;
break;
}
done += got;
}

I'm not a Swift user, so I'm afraid I can't advise on that, but the BASS calls should be much the same in any language.

soundgals

  • Posts: 63
Thanks so much again for your help Ian. I know you're not a Swift user; but I think I'll be able to translate your example.

Will post again once I've had a chance to try it.

soundgals

  • Posts: 63
So here is my attempt at translating your example to Swift…

func getDSDToPCMData(theURL:String)->Data{
        var trackData:Data? = nil
        let stream = BASS_DSD_StreamCreateURL(theURL, 0, DWORD(BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE), nil, nil, 0)
        var length:QWORD = BASS_ChannelGetLength(stream, DWORD(BASS_POS_BYTE))
        let bufferSize = length
        let buffer = UnsafeMutablePointer<UInt32>.allocate(capacity: Int(bufferSize))
                defer {
                    buffer.deallocate()
                }
        var done:QWORD  = 0
        while (done < length) {
            let got = BASS_ChannelGetData(DWORD(stream), buffer + UnsafeMutableRawPointer.Stride(done), 0xFFFFFFF)
            if (Int(got) == -1) { // error/end
                    length = done
                    break
                }
            done += UInt64(got)
        }
        trackData = Data(bytes: buffer, count: Int(length))
        return trackData!
       
    }

This appears to provide me with Audio data that I could perform DSP on. Before I attempt that, I tried to play the data through BASS by creating another URL Stream; but no stream could be created from this data.

Either I'm missing a further step, which would be needed to convert this to PCM/WAV data format, or I've made a mistake, or both.

The last two lines are there to convert the buffer pointers to Swift's "Data" format.

NOTE: I needed to use "BASS_SAMPLE_FLOAT" in order to retrieve data that was close to the same size as the original DSF data.

I'm using UnsafeMutablePointer<UInt32> because I believe I need Int pointers of that size.

Any further help will be appreciated, as always.

Ian @ un4seen

  • Administrator
  • Posts: 25725
How are you trying to create the new stream from the decoded data? As it's plain PCM data (without any headers to describe its format), you should be using BASS_StreamCreate.

Is your ultimate aim to play a file with some DSP applied? If so, it would usually be better to use BASS_ChannelSetDSP (and a DSPPROC callback) on the file's stream for that. Rather than decoding the entire file, applying the DSP to the decoded data, and then playing it.

soundgals

  • Posts: 63
How are you trying to create the new stream from the decoded data? As it's plain PCM data (without any headers to describe its format), you should be using BASS_StreamCreate.

Thanks, I realise it's plain PCM data, and I tried to feed this data into (non BASS) code that I already have for creating WAV files, including adding the necessary header; but the data wasn't accepted by this code. I think that's possibly because the data I usually feed to it is already formatted, and not plain PCM.

I did look at BASS_StreamCreate; but it isn't clear to me how I could create a new stream from the channel data I just retrieved, and apply the necessary WAV header file to produce the data equivalent of a WAV file.

Do I need to use one of the Encode functions; but without writing to a file? I just want to keep it as data.

Is your ultimate aim to play a file with some DSP applied? If so, it would usually be better to use BASS_ChannelSetDSP (and a DSPPROC callback) on the file's stream for that. Rather than decoding the entire file, applying the DSP to the decoded data, and then playing it.

I don't believe the DSP I want to apply can be applied within BASS. It is a complex up-sampling process and I already have all the code in place. I just need to feed in that data equivalent of a WAV file. Then I can use BASS to subsequently play the result.

Ian @ un4seen

  • Administrator
  • Posts: 25725
Are you sure the DSP requires a WAV header? That would be a bit unusual, but if so, I guess it processes an entire file at once rather than a stream of data? If you do need a WAV header, you could indeed use the BASSenc add-on, but if you want to receive the data in a callback (instead of a file) then it would need to be BASS_Encode_StartLimit rather than BASS_Encode_Start, so that you can specify the length up front because it won't be possible for BASSenc to update the WAV header at the end. Alternatively, if you're familiar with the WAV format then you can generate a WAV header yourself and put it in front of the data from BASS_ChannelGetData, which you can then update at the end if necessary. The RECTEST.C example included in the BASS package does this (but with data from a recording rather than BASS_ChannelGetData).

If the DSP can process a stream of data instead, then another option is to use BASS_StreamCreate and a STREAMPROC callback that fetches the required amount of data from the decoder (via BASS_ChannelGetData) for the DSP to produce the requested amount of output. This is how the BASS_FX add-on's tempo and reverse processing works, for example.

soundgals

  • Posts: 63
Thanks again Ian.

You're correct the DSP processes an entire file's worth of data. So yes, it does require the WAV header.

I'm embarrassed to admit that, thanks to you I realised I was missing the obvious. I already have a function to convert plain PCM to WAV. Applying that to the result of BASS_ChannelGetData gives me exactly what I need.