Namespace: Un4seen.Bass
Assembly: Bass.Net (in Bass.Net.dll) Version: 2.4.17.2
Parameters
- handle
- Type: SystemInt32
The stream that needs writing. - buffer
- Type: SystemIntPtr
The pointer to the buffer to write the sample data in. The sample data must be written in standard Windows PCM format - 8-bit samples are unsigned, 16-bit samples are signed, 32-bit floating-point samples range from -1 to 1. - length
- Type: SystemInt32
The number of bytes to write. - user
- Type: SystemIntPtr
The user instance data given when BASS_StreamCreate(Int32, Int32, BASSFlag, STREAMPROC, IntPtr) was called.
Return Value
Type: Int32The number of bytes written by the function, optionally using the BASS_STREAMPROC_END (BASSStreamProc) flag to signify that the end of the stream is reached.
A stream writing function should obviously be as quick as possible, because other streams (and MOD musics) can't be updated until it's finished.
It is better to return less data quickly, rather than spending a long time delivering the amount BASS requested.
Although a STREAMPROC may return less data than BASS requests, be careful not to do so by too much, too often. If the buffer level gets too low, BASS will automatically stall playback of the stream, until the whole buffer has refilled.
BASS_ChannelGetData(Int32, IntPtr, Int32) (BASS_DATA_AVAILABLE) can be used to check the buffer level, and BASS_ChannelIsActive(Int32) can be used to check if playback has stalled.
A BASS_SYNC_STALL sync can also be set via BASS_ChannelSetSync(Int32, BASSSync, Int64, SYNCPROC, IntPtr), to be triggered upon playback stalling or resuming.
If you do return less than the requested amount of data, the number of bytes should still equate to a whole number of samples.
Some functions can cause problems if called from within a stream (or DSP) function. Do not call these functions from within a stream callback:
BASS_Stop, BASS_Free, BASS_MusicLoad(String, Int64, Int32, BASSFlag, Int32), BASS_StreamCreate(Int32, Int32, BASSFlag, STREAMPROC, IntPtr) or any other stream creation functions.
Also, do not call BASS_StreamFree(Int32) or BASS_ChannelStop(Int32) with the same handle as received by the callback.
When streaming multi-channel sample data, the channel order of each sample is as follows:
3 channels: left-front, right-front, center.
4 channels: left-front, right-front, left-rear/side, right-rear/side.
6 channels(5.1): left-front, right-front, center, LFE, left-rear/side, right-rear/side.
8 channels(7.1): left-front, right-front, center, LFE, left-rear/side, right-rear/side, left-rear center, right-rear center.
It is clever to NOT alloc buffer data (e.g. a float[]) everytime within the callback method, since ALL callbacks should be really fast! And if you would do a 'float[] data = new float[]' every time here...the GarbageCollector would never really clean up that memory. Sideeffects might occure, due to the fact, that BASS will call this callback too fast and too often... However, this is not always the case, so in most examples it'll work just fine - but if you got problems - try moving any memory allocation things outside any callbacks.
NOTE: When you pass an instance of a callback delegate to one of the BASS functions, this delegate object will not be reference counted. This means .NET would not know, that it might still being used by BASS. The Garbage Collector might (re)move the delegate instance, if the variable holding the delegate is not declared as global. So make sure to always keep your delegate instance in a variable which lives as long as BASS needs it, e.g. use a global variable or member.
private STREAMPROC _myStreamCreate; // make it global, so that the GC can not remove it private byte[] _data; // local data buffer ... _myStreamCreate = new STREAMPROC(MyFileProc); FileStream _fs = File.OpenRead("test.raw"); int channel = Bass.BASS_StreamCreate(44100, 2, BASSFlag.BASS_DEFAULT, _myStreamCreate, IntPtr.Zero); Bass.BASS_ChannelPlay(channel, false); ... private int MyFileProc(int handle, IntPtr buffer, int length, IntPtr user) { // implementing the callback for BASS_StreamCreate... // here we need to deliver PCM sample data // increase the data buffer as needed if (_data == null || _data.Length < length) _data = new byte[length]; int bytesread = _fs.Read( _data, 0, length ); Marshal.Copy( _data, 0, buffer, bytesread ); if ( bytesread < length ) { bytesread |= (int)BASSStreamProc.BASS_STREAMPROC_END; // set indicator flag _fs.Close(); } return bytesread; }
unsafe private int MyFileProcUnsafe(int handle, IntPtr buffer, int length, IntPtr user) { // simply cast the given IntPtr to a native pointer to byte values byte *data = (byte*)buffer; // read the file into the data pointer directly int bytesread = length; for (int a=0; a < length; a++) { int val = _fs.ReadByte(); if (val != -1) { data[a] = (byte)val; // set the value } else { bytesread = a; break; } } // end of the file/stream? if ( bytesread < length ) { bytesread |= (int)BASSStreamProc.BASS_STREAMPROC_END; // set indicator flag _fs.Close(); } return bytesread; }