Author Topic: About to give up with this project...editing multi-channel Ogg file in memory?  (Read 592 times)

trojannemo

  • Posts: 143
Hi there. I'm glad to see this forum still up. I haven't posted in YEARS.
Bass and Bass.NET have been good to me. I use Bass.NET across multiple projects (all free, no income derived but as a hobby). I've managed to do quite a bit with Bass and I'm generally happy with it. I thought I was very familiar with it but this latest project has kicked my butt.

I'm reading a multichannel (channel amount varies) ogg file. While it's in memory, I need to merge some channels (for example, channels 0, 1, 2, 3 need to be merged into channels 0, 1) and the last two channels may need to be removed all together (i.e. I might start with a 16 channel file but need to end with a 12 channel file). I feel like this is possible. But my first attempts gave me very distorted ogg files with the wrong amount of channels, and by now I'm at a point where Bass refuses to write anything to file.

Is what I'm hoping to do even possible? If so, can I get some guidance? I want to do it in memory for a couple of reasons - faster and to eliminate lossy encoding issues. An ugly and slower (but I guess acceptable) alternative would be to output the channels to file (which I already the code for and i know it works), merge the ones that need merging, delete the ones that need deleting and then reconstructing the ogg file with the remaining files. But that is not something i've ever done with Bass. Can I input up to 18 channels at once and then output a multichannel ogg file? This is for a video game so there are additional metadata files that help me know what each channel is for and where it goes in order when building the ogg file if we go that route.

Thank you for any help you can provide. I've spent far too many hours before giving up and coming here for help. Thanks.

Ian @ un4seen

  • Administrator
  • Posts: 26172
I don't think it'll be possible to merge OGG channels in a lossless way, ie. without decoding and re-encoding. How are you currently merging the channels? If you aren't already using the BASSmix add-on's matrix mixing feature then I would suggest trying that, ie. create a mixer with the required number of output channels and then add the source OGG file to it with a matrix to merge/mix the channels as wanted. You can use the BASSenc_OGG add-on to encode the mix to a new OGG file in memory. It could look something like this:

Code: [Select]
decoder = BASS_StreamCreateFile(false, inputfile, 0, 0, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE); // create decoder for source file
mixer = BASS_Mixer_StreamCreate(freq, chans, BASS_SAMPLE_FLOAT | BASS_MIXER_END | BASS_STREAM_DECODE); // create mixer that ends when its source(s) does
BASS_Mixer_StreamAddChannel(mixer, decoder, BASS_MIXER_CHAN_MATRIX | BASS_MIXER_CHAN_NORAMPIN); // plug the decoder into it with matrix mixing enabled
// initialize matrix here
BASS_Mixer_ChannelSetMatrix(decoder, matrix); // set the matrix
BASS_Encode_OGG_Start(mixer, NULL, BASS_ENCODE_AUTOFREE, OggEncodeProc, NULL); // set an OGG encoder on the mixer
for (;;) { // processing loop
BYTE buf[20000]; // processing buffer
int got = BASS_ChannelGetData(mixer, buf, sizeof(buf)); // process the mixer
if (got < 0) break; // done
}
BASS_StreamFree(mixer); // free the mixer (and encoder due to AUTOFREE)

...

void CALLBACK OggEncodeProc(HENCODE handle, DWORD channel, const void *buffer, DWORD length, void *user)
{
// add "buffer" contents to file in memory
}

Please see the documentation for details on the mentioned functions.

trojannemo

  • Posts: 143
I think I forgot to specifically state that I'm a C# user so I'm running Bass.NET. I mention that in case some functions aren't featured in the .NET implementation.

Looking at your code, I'm fairly sure my issue is at the step of initializing the matrix. I have code that creates a matrix and applies panning and volume data to each channel just fine, but until now it's only needed to take the multichannel ogg, add or ignore some of the channels, and always end up either playing or saving to file a mono or stereo file. Never did I apply the matrix at a multi-channel level. So I'll have to rework that and see if it's possible.

In case that isn't feasible - what about my second solution? Is it possible to create a mixer and load channel after channel from file to create a multi-channel/interleaved ogg file, which i can then write to a single file?

Thanks again.

Ian @ un4seen

  • Administrator
  • Posts: 26172
I think I forgot to specifically state that I'm a C# user so I'm running Bass.NET. I mention that in case some functions aren't featured in the .NET implementation.

I'm not a C#/.Net user myself, so I can't help much with the specifics of that, but the code above should be very much the same. The only real difference will probably be in setting the encoder callback function. Please see the "Callbacks and Delegates" section of the "Interoperating with Unmanaged Code" page of the BASS.Net documentation for info on that stuff.

In case that isn't feasible - what about my second solution? Is it possible to create a mixer and load channel after channel from file to create a multi-channel/interleaved ogg file, which i can then write to a single file?

Do you mean mixing multiple source OGG files into a single output file? If so, that is possible too, with basically the same code as above. You would just need to add BASS_StreamCreateFile/BASS_Mixer_StreamAddChannel/BASS_Mixer_ChannelSetMatrix calls for each additional source.

trojannemo

  • Posts: 143
Yes I mean exactly that. I can easily and accurate take the multi channel ogg, separate it into component tracks and write to file. Then I would want to get one of them as the base, and start adding the other ones (ignoring the ones I want to "delete"). When I have the newly created multichannel ogg in memory, I would write that to file (since I'm going to need to further mess with the audio including changing the header and encrypting the file). This is inefficient and not very elegant but it would "work."

Can you show me how that would look with a couple of files? I ask because I already tried doing something similar in memory by creating individual bassmixers for each channel (without ever writing to disc) and the resulting bassmixer in theory should have worked because it had a negative value. But it would fail at the time of writing the buffer chunks. So obviously I did something wrong.

trojannemo

  • Posts: 143
SOLVED!

It really came down to the GetChannelMatrix function. I was (and have been for a decade) thinking 2-dimensionally because I always needed a stereo end file, no matter how many channels the input file had. After some more back and forth today it dawned on me that I wasn't accounting for the difference in input and output channels and was still thinking of 0-1 instead of 0-17 (max channels for my use case is 18). My code is very specific to this one game so I don't know how useful it would be to others for me to share the solution that worked, but if anyone has any questions in the future about this just message me or post here and i'll try to help.

This is the biggest thing that did it for me, even though I looked at it a million times, I wasn't putting two and two together:

//matrix must be float[output_channels, input_channels]
var matrix = new float[outputChans, inputChans];

Thanks Ian.

radio42

  • Posts: 4840
Hi,
Sorry for the late reply, but I just saw your email.
But I am glad, that you could solve your issue… I was just about to post the c# version of Ian‘s above code, which is pretty similar.
But glad you already found a solution!