-
Notifications
You must be signed in to change notification settings - Fork 17
BASS decoder
wo80 edited this page May 3, 2021
·
4 revisions
The decoder was tested on the .NET 5.0 platform using BASS.NET
. You will need Bass.Net.dll
, bass.dll
and bassmix.dll
from un4seen.com. The code for the AudioDecoder
base class can be found in the NAudio example.
namespace AcoustID.Examples
{
using System;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Mix;
/// <summary>
/// Decode using the Bass.Net library. Uses Bass to resample the audio, which
/// is faster than the AcoustId resampling.
/// </summary>
public class BassDecoder : AudioDecoder
{
int bassStream;
int bassMixer;
string file;
bool resample;
public BassDecoder(string file)
: this(file, false)
{
}
public BassDecoder(string file, bool resample)
{
this.file = file;
this.resample = resample;
// Open the BASS stream and keep it open until Dispose() is called. This might lock
// the file. A better approach would be to open the stream only when needed.
Initialize();
}
/// <summary>
/// Initialize Bass (call this on program startup).
/// </summary>
public static void InitializeBass()
{
BassNet.Registration("YOUR-MAIL", "YOUR-BASS-CODE");
try
{
// Load BASS (will throw if anything goes wrong, for example missing dlls).
Bass.BASS_Init(2, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
}
catch (Exception)
{
throw new NotSupportedException("BASS init failed.");
}
}
/// <summary>
/// Initialize Bass (call this on program exit).
/// </summary>
public static void FreeBass()
{
Bass.BASS_Free();
}
/// <summary>
/// Decode an audio file.
/// </summary>
public override bool Decode(IAudioConsumer consumer, int maxLength)
{
if (bassStream == 0)
{
return false;
}
// Get the right stream:
int stream = this.resample ? bassMixer : bassStream;
int remaining, size, length;
short[] data = new short[BUFFER_SIZE];
// Samples to read to get maxLength seconds of audio
remaining = maxLength * this.sampleRate * this.channels;
// Bytes to read
length = 2 * Math.Min(remaining, BUFFER_SIZE);
while (Bass.BASS_ChannelIsActive(stream) == BASSActive.BASS_ACTIVE_PLAYING)
{
size = Bass.BASS_ChannelGetData(stream, data, length);
if (size > 0)
{
consumer.Consume(data, size / 2);
remaining -= size / 2;
if (remaining <= 0)
{
break;
}
length = 2 * Math.Min(remaining, BUFFER_SIZE);
}
}
return true;
}
private void Initialize()
{
// Create a stream channel from a file (use BASS_STREAM_PRESCAN for mp3?)
bassStream = Bass.BASS_StreamCreateFile(file, 0L, 0L, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_ASYNCFILE);
if (bassStream == 0)
{
throw new Exception("Could not open BASS stream: error code = " + Bass.BASS_ErrorGetCode());
}
var info = Bass.BASS_ChannelGetInfo(bassStream);
this.sampleRate = info.freq;
this.channels = info.chans;
double duration = Bass.BASS_ChannelBytes2Seconds(bassStream, Bass.BASS_ChannelGetLength(bassStream));
this.Format = new AudioProperties(info.freq, info.Is8bit ? 8 : (info.Is32bit ? 32 : 16),
info.chans, (int)duration);
if (this.Format.BitDepth != 16)
{
Dispose(true);
return;
}
if (this.resample)
{
this.sampleRate = 11025;
this.channels = 1;
// Create resample stream.
bassMixer = BassMix.BASS_Mixer_StreamCreate(this.sampleRate, this.channels,
BASSFlag.BASS_MIXER_END | BASSFlag.BASS_STREAM_DECODE);
if (bassMixer != 0)
{
BassMix.BASS_Mixer_StreamAddChannel(bassMixer, bassStream, 0);
}
}
}
#region IDisposable implementation
private bool _disposed = false;
protected override void Dispose(bool disposing)
{
if (_disposed) return;
if (bassStream != 0)
{
Bass.BASS_StreamFree(bassStream);
bassStream = 0;
}
if (resample && bassMixer != 0)
{
Bass.BASS_StreamFree(bassMixer);
bassMixer = 0;
}
_disposed = disposing;
}
#endregion
}
}