Ian,
I need your in-depth knowledge of BASS to come in and help take my code to the finish line. I have literally spent three straight days trying all kinds of things.
Here's where the code stands now:
case ".m4a":
isM4A = true;
//default to Apple ALAC codec first
BassStream = BassAlac.BASS_ALAC_StreamCreateFile(input_file, 0L, File.ReadAllBytes(input_file).Length, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT);
if (BassStream != 0) break;
//if that failed then try AAC codec second
BassStream = BassAac.BASS_AAC_StreamCreateFile(input_file, 0L, File.ReadAllBytes(input_file).Length, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT);
if (BassStream != 0) break;
//if that failed then try MP4 third
BassStream = BassAac.BASS_MP4_StreamCreateFile(input_file, 0L, File.ReadAllBytes(input_file).Length, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT);
if (BassStream != 0) break;
//if that failed then this might be a Fortnite .m4a Opus file, let's try to work with it
BassStream = ProcessFortniteFile(input_file);
isFnF = BassStream != 0;
break;
.....
ExtractOpusDataFromMp4(file, opusData);
opusSamples = opusData.ToArray();
return FortniteFestivalRawToBassStream();
At this point, I have a byte array of raw Opus data samples. Each sample is preceeded by a 4 byte size identifier so we don't have to guess how much to read from the byte array. I have confirmed the data is correct and the sample sizes are correct. This particular test file is 9932 samples 12.xxMB large.
private static int FortniteFestivalRawToBassStream()
{
BASS_OPUS_HEAD head = new BASS_OPUS_HEAD
{
version = 1,
channels = 10,
preskip = 312,
inputrate = 44100,
gain = 0,
mapping = 255,
streams = 5,
coupled = 5,
chanmap = new byte[] { }
};
head.chanmap = new byte[255];
for (int i = 0; i < head.channels; i++)
{
head.chanmap[i] = (byte)i;
}
int streamHandle = BASS_OPUS_StreamCreate(ref head, (int)(BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT), STREAMPROC_PUSH, IntPtr.Zero);
if (streamHandle == 0)
{
Console.WriteLine("Failed to create Opus stream. Error: " + Bass.BASS_ErrorGetCode());
return 0;
}
// Push Opus packets to the stream
PushOpusPacketsToStream(streamHandle);
return streamHandle;
}
private static void PushOpusPacketsToStream(int streamHandle)
{
GCHandle handle = GCHandle.Alloc(opusSamples, GCHandleType.Pinned);
try
{
IntPtr pointer = handle.AddrOfPinnedObject();
int offset = 0;
while (offset < opusSamples.Length)
{
if (offset + 4 > opusSamples.Length)
{
Console.WriteLine("Not enough data for size identifier.");
break;
}
int packetSize = BitConverter.ToInt32(opusSamples, offset);
offset += 4;
if (packetSize <= 0 || offset + packetSize > opusSamples.Length)
{
Console.WriteLine("Invalid or incomplete packet data.");
break;
}
int result = BASS_OPUS_StreamPutData(streamHandle, IntPtr.Add(pointer, offset), packetSize);
if (result == -1)
{
Console.WriteLine("Failed to push data. Error: " + Bass.BASS_ErrorGetCode());
break;
}
offset += packetSize;
}
// Confirm all data is pushed before ending the stream
if (offset >= opusSamples.Length)
{
BASS_OPUS_StreamPutData(streamHandle, IntPtr.Zero, (int)BASSStreamProc.BASS_STREAMPROC_END);
}
else
{
Console.WriteLine("Stream ended prematurely. Offset: " + offset);
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
}
finally
{
handle.Free();
}
}
So this pushes all the Opus samples into the stream, which is returned into the original function so now BassStream is streamHandle.
BassStream is fed into a BassMixer that does a few things, including downmixing and setting a channel matrix.
Then I create an encoder (depends on the user but my test case is MP3) and I use the following to write to file:
const int MaxBufferSize = 1048576; // 1MB, adjust based on available memory and requirements
while (true)
{
var buffer = new byte[MaxBufferSize];
var c = Bass.BASS_ChannelGetData(BassMixer, buffer, buffer.Length);
if (c < 0) break;
}
This whole thing "works" in the sense that there is no error and valid audio data is written to file. But only 44 seconds of audio is written, then the audio ends abruptly.
Obviously I'm missing something but I don't know what. Hopefully you can find the obvious mistake.
Thanks in advance.
EDIT: After further debugging, same file, I can tell you that I am pushing 12585050 bytes into the channel, but the channel is only writing 8581439 bytes to file. These numbers are consistent from run to run.