Author Topic: Working with multi-channel OPUS files  (Read 302 times)

nequam

  • Posts: 4
Working with multi-channel OPUS files
« on: 14 Jul '17 - 16:51 »
I'm working in a karaoke related project, and I was wondering that maybe I could use BASS for our needs. Our music supplier is providing us OPUS files with separate channels for vocals and instrumental tracks, and I'd like to create an audio player for iOS that can play both of channels simultaneously, and according to user input increase/decrease the volume of the vocals track.

So far I've managed to open a file with BASS_OPUS_StreamCreateFile, but when I use BASS_ChannelPlay I can only hear the instrumental track.

I've searched through the API, but I'm clueless how to access the other channel.

The music file that I've been using for debugging can be found here: http://jhsipola.kapsi.fi/example.opus

Ian @ un4seen

  • Administrator
  • Posts: 20401
Re: Working with multi-channel OPUS files
« Reply #1 on: 14 Jul '17 - 17:36 »
You could use the BASSmix add-on to downmix the file to stereo with whatever levels you want. For example, something like this:

Code: [Select]
source=BASS_StreamCreateFile(FALSE, filename, 0, 0, BASS_STREAM_DECODE|BASS_SAMPLE_FLOAT); // create a decoder for the OPUS file
BASS_CHANNELINFO info;
BASS_ChannelGetInfo(source, &info); // get its info
mixer=BASS_Mixer_StreamCreate(info.freq, 2, BASS_MIXER_END|BASS_SAMPLE_FLOAT); // create a stereo mixer with the same sample rate
BASS_Mixer_StreamAddChannel(mixer, source, BASS_MIXER_MATRIX); // plug the OPUS file into it with matrix mixing enabled
float matrix[2][3] = { // mixing matrix for 2 outputs and 3 inputs
{0.5, 0, 0.5}, // left out = 50% left in + 50% center in
{0, 0.5, 0.5} // right out = 50% right in + 50% center in
};
BASS_Mixer_ChannelSetMatrix(source, matrix); // apply the matrix
BASS_ChannelPlay(mixer, 0); // start the mixer

If you need to seek during playback, you should also clear the mixer's buffer then to avoid any delay. You can do that by calling BASS_ChannelSetPosition on the mixer with pos=0, or BASS_ChannelPlay with restart=TRUE. For example, like this:

Code: [Select]
BASS_ChannelPause(mixer); // pause the mixer
BASS_Mixer_ChannelSetPosition(source, seekpos, BASS_POS_BYTE); // set the source's position
BASS_ChannelPlay(mixer, TRUE); // resume the mixer with buffer cleared

Please see the documentation for details on the mentioned functions.

nequam

  • Posts: 4
Re: Working with multi-channel OPUS files
« Reply #2 on: 18 Jul '17 - 10:48 »
For some reason when I set the matrix to the channel it kills the whole sound system of my iPhone and reboot is required for restore :D Though sometimes when I use the simulator I can hear extremely distorted melody with lots of noise.

Ian @ un4seen

  • Administrator
  • Posts: 20401
Re: Working with multi-channel OPUS files
« Reply #3 on: 18 Jul '17 - 18:10 »
Are you getting the crash with the OPUS file that you uploaded? The 2x3 matrix in the code above is assuming that the file has 3 channels, which is what your uploaded file had. You can use BASS_ChannelGetInfo to check how many channels there are (see "chans").

nequam

  • Posts: 4
Re: Working with multi-channel OPUS files
« Reply #4 on: 19 Jul '17 - 16:46 »
Yeah, it's the same file.

Here's the full code I'm running:
Code: [Select]
import Foundation
import Bass

class KGPlayer: NSObject {
   
    var source: HSTREAM!
    var mixer: HSTREAM!
    var matrix: [[Float]] = [[0.5, 0, 0.5], [0, 0.5, 0.5]]
    var channelInfo: BASS_CHANNELINFO = BASS_CHANNELINFO()
   
    override init() {
        BASS_LoadPlugins()
        BASS_Init(-1, 44100, 0, nil, nil)
        BASS_SetConfig(DWORD(BASS_CONFIG_IOS_NOCATEGORY), 1)
        super.init()
    }
   
    func loadItem(from url: URL) {
        source = BASS_StreamCreateFile(false, url.path.cString(using: .utf8)!, 0, 0, DWORD(BASS_STREAM_DECODE)|DWORD(BASS_SAMPLE_FLOAT))
       
        BASS_ChannelGetInfo(source, &channelInfo)
        mixer = BASS_Mixer_StreamCreate(channelInfo.freq, 2, DWORD(BASS_MIXER_END)|DWORD(BASS_SAMPLE_FLOAT))
       
        BASS_Mixer_StreamAddChannel(mixer, source, DWORD(BASS_MIXER_MATRIX))
        BASS_Mixer_ChannelSetMatrix(source, matrix)
    }
   
    func play() {
        BASS_ChannelPlay(mixer, false)
    }
}

Ian @ un4seen

  • Administrator
  • Posts: 20401
Re: Working with multi-channel OPUS files
« Reply #5 on: 20 Jul '17 - 17:49 »
It looks like you're using Swift. I'm not a Swift user myself, but I tried the C code that I posted earlier on an iOS device and it seems to work fine. Perhaps the Swift "matrix" array doesn't have the same memory layout? To confirm whether the matrix is causing the problem, does removing the BASS_Mixer_ChannelSetMatrix call prevent it happening? If so, perhaps you could try flattening the array, something like this:

Code: [Select]
var matrix: [Float] = [0.5, 0, 0.5, 0, 0.5, 0.5]

nequam

  • Posts: 4
Re: Working with multi-channel OPUS files
« Reply #6 on: 24 Jul '17 - 06:53 »
That was exactly the issue:) Thanks man!