bassaudio
v1.1.1
Published
un4seen Bass Audio Library based on ffi bindings
Downloads
65
Readme
Un4Seen Bass Audio Library Wrapper
Bass Audio library is the best audio library to play, edit, convert, stream etc. this wrapper wraps most of the "audio playback" features using ffi , ref , ref-struct .
ffi enables to call c library methods, properties , callbacks etc.
Compatible with?
tested on MacosX El capitan, windows 32 and 64 bits, ubuntu 14+, raspberrypi 2-3 (armv7), not tested on armv6 but its easy to test, just install and see.. related platform binaries can be downloaded from bass web page..
Warning
Please put the required bass audio library files to root
folder (dll
files for windows, so
files for *nix, dylib
files for macos)
Download the required dll files from http://www.un4seen.com. i did not include the required files in this module..
Read full documentation of bass from its original help. i Will only write same simple examples.
Features:
BASS_Init
BASS_GetVersion
BASS_StreamCreateFile
BASS_StreamCreateURL
BASS_StreamFree
BASS_ChannelPlay
BASS_ChannelStop
BASS_ChannelPause
BASS_ChannelGetPosition
BASS_ChannelSetPosition
BASS_ChannelGetLength
BASS_ChannelBytes2Seconds
BASS_ChannelSeconds2Bytes
BASS_ChannelGetLevel
BASS_ChannelRemoveSync
BASS_ChannelIsActive
BASS_ChannelSetAttribute
BASS_ChannelGetAttribute
BASS_ChannelSetSync
BASS_ChannelSlideAttribute
BASS_ChannelIsSliding
BASS_ChannelGetDevice
BASS_ChannelSetDevice
BASS_StreamFree
BASS_SetDevice
BASS_SetVolume
BASS_Start
BASS_Stop
BASS_Pause
BASS_GetInfo
BASS_ErrorGetCode
BASS_Free
BASS_GetCPU
BASS_GetDevice
BASS_GetDeviceInfo
BASS_ChannelGetTags
BASS_Mixer_StreamCreate
BASS_Mixer_StreamAddChannel
BASS_Mixer_ChannelGetLevel
BASS_Mixer_ChannelGetMixer
BASS_Mixer_ChannelGetPosition
BASS_Mixer_ChannelRemove
BASS_Mixer_ChannelRemoveSync
BASS_Mixer_ChannelSetPosition
BASS_Mixer_ChannelSetSync
BASS_Encode_Start
BASS_Encode_IsActive
BASS_Encode_SetNotify
BASS_Encode_SetPaused
BASS_Encode_Stop
BASS_Encode_CastInit
BASS_Encode_CastGetStats
BASS_Encode_CastSetTitle
BASS_Split_StreamCreate
BASS_Split_StreamGetAvailable
BASS_Split_StreamGetSource
BASS_Split_StreamGetSplits
BASS_Split_StreamReset
BASS_Split_StreamResetEx
Extra:
SYNCPROC also implemented
Installation
Install with npm :
npm install bassaudio
Examples
basic load and play file
var bass=require('bassaudio');
var basslib=new bass();
//get all sound cards
var cards=basslib.getDevices();
console.log('total found sound cards:' + cards.length)
//lets print first sound card's info, find out more inside source code..
//first item in array '[0]' is "no sound" , then use the item [1]
//you will see that card isInitialized will be false, because we did not init it yet..
var card=cards[1];
console.log(card.name + ' is enabled:' + card.enabled + ' ,IsDefault:' + card.IsDefault + ' , IsInitialized:' + card.IsInitialized + ' ,typeSpeakers:' + card.typeSpeakers)
// [device],[freq],[flags] , -1 is default sound card
var result=basslib.BASS_Init(-1,44100,basslib.BASS_Initflags.BASS_DEVICE_STEREO)
if(!result){
console.log('error init sound card:' + basslib.BASS_ErrorGetCode())
}
console.log("first card is init?:" + basslib.getDevice(1).IsInitialized)
// isMemoryFile,filename,offset,length,flags, returns pointer of file
var chan=basslib.BASS_StreamCreateFile(0,'c:\\mp3\\test.mp3',0,0,0)
if(basslib.BASS_ErrorGetCode()!=basslib.BASS_ErrorCode.BASS_OK){
console.log('error opening file:' + basslib.BASS_ErrorGetCode())
}
//lets play
//channel,restart , returns (also there are stop , pause commands)
var success=basslib.BASS_ChannelPlay(chan,-1)
if(!success){
console.log('error playing file:' + basslib.BASS_ErrorGetCode())
}
Get Duration
//get the duration, bass returns the total bytes of the channel pointer, then we must convert it to seconds :)
var durationInBytes= basslib.BASS_ChannelGetLength(chan,0)
var durationInSeconds=basslib.BASS_ChannelBytes2Seconds(chan,durationInBytes)
Get Duration Example2
//if stream is active (playing), then get position and duration..
setInterval(function(){
if(basslib.BASS_ChannelIsActive(chan)==basslib.BASS_ChannelIsActiveAttribs.BASS_ACTIVE_PLAYING) {
var position = basslib.BASS_ChannelBytes2Seconds(chan, basslib.BASS_ChannelGetPosition(chan, 0));
var duration = basslib.BASS_ChannelBytes2Seconds(chan, basslib.BASS_ChannelGetLength(chan, 0))
console.log(position + ' / ' + duration)
}else{
console.log('stopped' )
}
},500)
Get Volume of channel
var ref=require('ref')
//set a float pointer
var volume=ref.alloc('float');
//get the volume
var result=basslib.BASS_ChannelGetAttribute(chan,basslib.BASS_ChannelAttributes.BASS_ATTRIB_VOL,volume);
//now deref volume to get the value
console.log(ref.deref(volume))
Set Volume
//lets set to 0.3
basslib.BASS_ChannelSetAttribute(chan,basslib.BASS_ChannelAttributes.BASS_ATTRIB_VOL,0.3)
Get current Position of playback
var positionInBytes= basslib.BASS_ChannelGetPosition(chan,0);
//now convert it to seconds
var position=basslib.BASS_ChannelBytes2Seconds(chan,positionInBytes);
Set Position
//first get the byte position of desired seconds (ex:to last 10 seconds
var Last10SecondsBytePos=basslib.BASS_ChannelSeconds2Bytes(chan,durationInSeconds-10);
basslib.BASS_ChannelSetPosition(chan,Last10SecondsBytePos);
Is channel is playing?
var result=basslib.BASS_ChannelIsActive(chan)
if(result==basslib.BASS_ChannelIsActiveAttribs.BASS_ACTIVE_PLAYING){
console.log('channel is playing')
}
sliding
//Lets slide volume to 0 in 3 seconds (3000 milliseconds)
basslib.BASS_ChannelSlideAttribute(chan,basslib.BASS_ChannelAttributes.BASS_ATTRIB_VOL,0,3000)
callback
//lets make a callback when position reaches to 20. seconds.
var Pos20SecondsBytePos=basslib.BASS_ChannelSeconds2Bytes(chan,20);
var proc20SecondsID=basslib.BASS_ChannelSetSync(chan,basslib.BASS_ChannelSyncTypes.BASS_SYNC_POS,Pos20SecondsBytePos,function(handle,channel,data,user){
if(handle==proc20SecondsID){
console.log('position reached to the 20 seconds..')
}
})
//lets get the event when the position reaches to end
var procTOENDID=basslib.BASS_ChannelSetSync(chan,basslib.BASS_ChannelSyncTypes.BASS_SYNC_END,0,function(handle,channel,data,user){
if(handle==procTOENDID){
console.log('playback finished..')
}
})
vumeter
//lets get vumeter :)
var levels=basslib.BASS_ChannelGetLevel(chan);
//its a 64 bit value, lets get lo and hiwords
var rightlevel=basslib.toFloat64(levels)[0]
var leftlevel=basslib.toFloat64(levels)[1]
close the file
if(basslib.BASS_ChannelIsActive(chan)==basslib.BASS_ChannelIsActiveAttribs.BASS_ACTIVE_PLAYING){
//stop the channel
basslib.BASS_ChannelStop(chan);
}
var result=basslib.BASS_StreamFree(chan)
if(!result){
console.log('stream free error:' + basslib.BASS_ErrorGetCode())
}
change sound card
//first check if this sound card is initialized
var soundCardIndex=1;
var info=basslib.getDevice(soundCardIndex);
if(!info.enabled){
console.log('this device is not enabled')
}
if(!info.IsInitialized){
var result= basslib.BASS_Init(soundCardIndex,44100,basslib.BASS_Initflags.BASS_DEVICE_STEREO);
if(result!=basslib.BASS_ErrorCode.BASS_OK){
console.log('error init sound card:' + basslib.BASS_ErrorGetCode())
}
}
var success=basslib.BASS_ChannelSetDevice(chan,soundCardIndex)
if(!success){
console.log('error init sound card:' + basslib.BASS_ErrorGetCode())
}
Info of a channel
var info= basslib.BASS_ChannelGetInfo(chan);
console.log('info.ctype:' + info.ctype)
console.log("is channel an mp3 stream?:" + (info.ctype ==basslib.BASS_CHANNELINFOtypes.BASS_CTYPE_STREAM_MP3))
//other infos are: freq,chans,flags,ctype,origres,plugin,sample,filename
Info of a device
var info= basslib.getInfo();
console.log('speaker count:' + info.speakers)
console.log('minimum buffer:' + info.minbuf)
console.log('latency:' + info.latency)
Free the memory from bass
basslib.BASS_Free();
MIXER FEATURES
Enable Mixer
//before using mixer, first enable it. and put the required component to root folder.
basslib.EnableMixer(true);
Mixer is Enabled?
//before using mixer, first enable it. and put the required component to root folder.
console.log(basslib.MixerEnabled());
Create mixer stream
basslib.EnableMixer(true);
var mixer=basslib.BASS_Mixer_StreamCreate(44100, 2,basslib.BASSFlags.BASS_SAMPLE_FLOAT);
var chan1=basslib.BASS_StreamCreateFile(0,'d:\\mp3\\tes1.mp3',0,0,basslib.BASSFlags.BASS_STREAM_DECODE | basslib.BASSFlags.BASS_SAMPLE_FLOAT)
var chan2=basslib.BASS_StreamCreateFile(0,'d:\\mp3\\test2.mp3',0,0,basslib.BASSFlags.BASS_STREAM_DECODE | basslib.BASSFlags.BASS_SAMPLE_FLOAT)
var ok1 = basslib.BASS_Mixer_StreamAddChannel(mixer, chan1, basslib.BASSFlags.BASS_SAMPLE_DEFAULT);
var ok2 = basslib.BASS_Mixer_StreamAddChannel(mixer, chan2, basslib.BASSFlags.BASS_SAMPLE_DEFAULT);
basslib.BASS_ChannelPlay(mixer,0)
Get current Position of mixer playback
var positionInBytes= basslib.BASS_Mixer_ChannelGetPosition(chan,0);
//now convert it to seconds
var position=basslib.BASS_ChannelBytes2Seconds(chan,positionInBytes);
ENCODER FEATURES
you can directly encode and send output to shotcast and icecast servers
use mixer as a trick, because if the channel freed or added new channel, the encoder stops itself.
add channels to mixer every time, and add mixer channel to encoder. so the encoder never stops..
Init encoder
basslib.EnableMixer(true);
var mixer=basslib.BASS_Mixer_StreamCreate(44100, 2,basslib.BASSFlags.BASS_SAMPLE_FLOAT);
var chan=basslib.BASS_StreamCreateFile(0,'d:\\mp3\\tes1.mp3',0,0,basslib.BASSFlags.BASS_STREAM_DECODE | basslib.BASSFlags.BASS_SAMPLE_FLOAT)
var ok = basslib.BASS_Mixer_StreamAddChannel(mixer, chan, basslib.BASSFlags.BASS_SAMPLE_DEFAULT);
basslib.EnableEncoder(true);
//lets try icecast
var enc_chan=basslib.BASS_Encode_Start(mixer,'lame -r -m s -s 22050 -b 56 -',basslib.BASS_Encode_Startflags.BASS_ENCODE_NOHEAD);
var result=basslib.BASS_Encode_CastInit(enc_chan,
'http://server-ip:8000/test',
'password',
basslib.BASS_Encode_CastInitcontentMIMEtypes.BASS_ENCODE_TYPE_MP3,
'test stream',
'http://your-server-ip',
'pop',
'this is my new icecast test',
'header1\r\nheader2\r\n',
44100,
true //public
);
basslib.BASS_ChannelPlay(mixer,0);
get notification from encoder server
var result=basslib.BASS_Encode_SetNotify(enc_chan,function(handle,status,user){
if(status==basslib.EncoderNotifyStatus.BASS_ENCODE_NOTIFY_CAST){
console.log('server connection is dead');
});
mono speaker output
//lets say if you have 5.1 speaker and want to use each output stereo or mono
//basically with 5.1 output you can use 6 different output channels.
//this example shows how to do it
var bass=require('bassaudio')
var basslib=new bass();
//set init to speakers
var result=basslib.BASS_Init(-1,44100,basslib.BASS_Initflags.BASS_DEVICE_SPEAKERS)
if(!result){
console.log('error init sound card:' + basslib.BASS_ErrorGetCode())
}
//to use mixer feature, you have to enable it
basslib.EnableMixer(true);
var cards=basslib.getDevices();
var speakerCount=basslib.getInfo().speakers;
console.log('we have',speakerCount,'speakers')
var path=require('path')
var f1=path.join(__dirname, '1.mp3')
//create a mixer and tell it to output front right
var mixer=basslib.BASS_Mixer_StreamCreate(44100, 1,basslib.BASS_SPEAKERtypes.BASS_SPEAKER_FRONTRIGHT);
console.log('mixer:',mixer,' error code:',basslib.BASS_ErrorGetCode())
//set channel to decode
var chan1=basslib.BASS_StreamCreateFile(0,f1,0,0,basslib.BASSFlags.BASS_STREAM_DECODE )
//if your file is stereo , you have to downmix to mono, else you cannot get it mono output to only 1 speaker.
var ok1 = basslib.BASS_Mixer_StreamAddChannel(mixer, chan1, basslib.BASS_MIXERsourceflags.BASS_MIXER_DOWNMIX);
var ok2=basslib.BASS_ChannelPlay(mixer,0)
console.log('chan1:',chan1,' error code:',basslib.BASS_ErrorGetCode())
console.log('ok1:',ok1,' error code:',basslib.BASS_ErrorGetCode())
setInterval(()=>{},1000)
splitting channels
//if you want to add a mixer to another mixer, it is not possible/supported
//but there is another way to do it.
//you can split any channel or mixer into two, then you can use the new splitted channel on any other mixer.
//A "splitter" basically does the opposite of a mixer: it splits a single source into multiple streams rather then mixing multiple sources into a single stream. Like mixer sources, splitter sources must be decoding channels.
var splitChan=basslib.BASS_Split_StreamCreate(mixer,0)
var splits=basslib.BASS_Split_StreamGetSplits(mixer)
var splitsource=basslib.BASS_Split_StreamGetSource(splitChan)
var avail1=basslib.BASS_Split_StreamGetAvailable(splitChan)
var avail2=basslib.BASS_Split_StreamGetAvailable(mixer)
INFO i only added methods, properties what i needed.. add yours to the code or send me mail..
IMPORTANT
Below modifications are now handled internally. You shouldn't need to do these manually. However, if you do, you know where to find them.
2017-02-10
please modify node_modules/ffi/lib/callback.js if you are using callbacks. ffi garbage collector removes callbacks after 10 seconds. with this modification, callbacks stays on memory
at line 81 , before return, add;
Object.defineProperty(func, '_func', {value:callback});
--------------------------------
2017-01-31
please modify node_modules/ffi/lib/library.js if you are using linux os and if you are using addons. ffi loads bass into instance, not as global instance.. so the addons could not find main bass on memory. this fixes that issue.. (on windows and macos, it works without this modification)
find
var dl = new DynamicLibrary(libfile || null, RTLD_NOW)
change it to
var dl = new DynamicLibrary(libfile || null, RTLD_NOW | DynamicLibrary.FLAGS.RTLD_GLOBAL)
UPDATE LOG
--------------------------------
1.1.1
bugfix..
1.1.0
updates:
its been too much time passed since i created this library. i was using it when node was 8.x.x , so some libraries are outdated. updated to the latest ffi and ref libraries with newer napi implementation. as you know, ffi and ref are the gateway between c, c++ libraries and nodejs.. so what changed? just 3 libs and 3 lines.. yep.. now it supports newer nodejs versions..
var Struct = require('ref-struct-napi'); var ref = require('ref-napi'); var ffi = require('ffi-napi');
thanks to RiccardoBiemmi the creator of bassaudiolibrary while i was not here, he updated the code.. but he just added his needs.. anyway, use his, or my work.. its up to you.. and add new features..
1.0.8
new features added:
BASS_Split_StreamCreate
BASS_Split_StreamGetAvailable
BASS_Split_StreamGetSource
BASS_Split_StreamGetSplits
BASS_Split_StreamReset
BASS_Split_StreamResetEx
1.0.7
- BASS_SetConfigflags, BASS_SetConfig, BASS_GetConfig, BASS_Update, BASS_ChannelUpdate features added
1.0.6
- correct an arg type in BASS_Encode_CastInit handler
1.0.5
- lock installed "ffi" version
- add shim to automate the changes to the "ffi" source code
1.0.4
- BASS_Encode_CastSetTitle has a minor bug. fixed.
1.0.3
- constructor now accepts and options object
- examples folder comes with link files
1.0.2
- getVolume() gives ref error when channel is 0. fixed.
1.0.1
- some types fixed for raspberrypi compability.
- examples folder added
- mono speaker out example added
1.0.0 Release
- i hope i fixed everything :) lets cross the fingers..
1.0.0-rc.22
- some types in BASS_StreamCreateFile are changed to ref.types.int64
1.0.0-rc.21
- these fixes are for linux environment
- fixed BASS_ChannelBytes2Seconds
- fixed BASS_ChannelSeconds2Bytes
- fixed BASS_ChannelSetPosition
1.0.0-rc.20
- added BASS_MIXERsourceflags
- found a bug on ffi source code, see above change for linux os..
1.0.0-rc.18
- BASS_ChannelGetAttribute returns float pointer. fixed.see getvolume example for usage.
- BASS_GETInfo now works.
1.0.0-rc.17
- BASS_Encode_IsActive fixed
1.0.0-rc.16
- getDevice(-1) must find the default sound card and return it.
- BASS_GetDevice takes 1 argument, but missing in code. fixed.
1.0.0-rc.15
- documentation fix
1.0.0-rc.14
- documentation fix
1.0.0-rc.13
new mixer features:
- BASS_Mixer_StreamCreate
- BASS_Mixer_StreamAddChannel
- BASS_Mixer_ChannelGetLevel
- BASS_Mixer_ChannelGetMixer
- BASS_Mixer_ChannelGetPosition
- BASS_Mixer_ChannelRemove
- BASS_Mixer_ChannelRemoveSync
- BASS_Mixer_ChannelSetPosition
- BASS_Mixer_ChannelSetSync
new encoder features:
- BASS_Encode_Start
- BASS_Encode_IsActive
- BASS_Encode_SetNotify
- BASS_Encode_SetPaused
- BASS_Encode_Stop
- BASS_Encode_CastInit
- BASS_Encode_CastGetStats
- BASS_Encode_CastSetTitle
1.0.0-rc.12
- BASS_ChannelBytes2Seconds and BASS_ChannelSeconds2Bytes returns wrong on arm cpu. pos value type changed to int64. now works correctly