Hello,
1st Question :
I have been doing some trials on creating a stream that behaves like a turntable, so which tempo can be changed, but direction as well.
I am currently having an issue when reversing the stream, depending the "dec_block" parameter, when I change its direction, it's not straight.
After changing the direction, I can still hear (for a short time) the stream playing in the previous direction, then I hear it playing the new direction normally. It looks like it does play the last buffer in the previous direction before playing the new direction.
2nd question :
What is from your point of view, and BASS internals, the best way to create such a stream with the API ?
So far, here are the methods I can create a stream behaving like that :
- Original decode stream -> Tempo stream -> Reverse stream
- Original decode stream -> Reverse stream -> Tempo stream
- Original decode stream -> Reverse stream -> BassAsio:BASS_ASIO_ChannelSetRate
With method 1,
Getting stream position (from reverse one) is slow and seems related to "dec_block" setting; (you get a new position every dec_block)
I did use the same value as in your documentation (2 seconds), a lower setting makes it faster but not comparable to getting a position from a simple stream you directly play.
Method 2,
This method will be useful when I will want to upgrade to use any protocol (DirectSound, Asio, Wasapi)
Method 3,
So far this method applies only for Asio though it works fine, I guess the resampling is done right on the unmanaged part of BASS.
The method 2, 3 works much better but I am still having the issue in question 1.
On the sample I set dec_block to 30 because you can hear it more obviously at this setting.
(Update : The issue occurs only when going from forward to reverse)
Any ideas ?
Many thanks.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Fx;
using Un4seen.BassAsio;
namespace pitchSetting
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private WindowInteropHelper _helper;
private ASIOPROC _procAsio;
private SYNCPROC _procSync;
private int _pStream;
private int _pReverse;
public MainWindow()
{
InitializeComponent();
Closing += WindowClosing;
_procSync = SyncProc;
_procAsio = AsioProc;
}
private void WindowLoaded(object sender, RoutedEventArgs e)
{
_helper = new WindowInteropHelper(this);
if (!Bass.BASS_Init(0, 44100, BASSInit.BASS_DEVICE_DEFAULT, _helper.Handle) || !BassAsio.BASS_ASIO_Init(0))
return;
// Create the original decoding stream and the reverse stream
string file = @"D:\Music\Singles\House\Tribal\Dub\Francois K. - Edge Of Time (Tee's Freeze Mix).mp3";
_pStream = Bass.BASS_StreamCreateFile(file, 0, 0, BASSFlag.BASS_STREAM_PRESCAN | BASSFlag.BASS_STREAM_DECODE);
_pReverse = BassFx.BASS_FX_ReverseCreate(_pStream, 30f, BASSFlag.BASS_STREAM_DECODE);
bool b = Bass.BASS_ChannelSetAttribute(_pReverse, BASSAttribute.BASS_ATTRIB_REVERSE_DIR, 1);
// Set a sync for end so we pause it, and we will unpause when changing direction (just like a real CDJ), otherwise it won't do it
IntPtr user = new IntPtr(_pReverse);
int sync = Bass.BASS_ChannelSetSync(_pReverse, BASSSync.BASS_SYNC_MIXTIME | BASSSync.BASS_SYNC_END, 0, _procSync, user);
// Start Asio
bool bassAsioChannelEnable = BassAsio.BASS_ASIO_ChannelEnable(false, 0, _procAsio, user);
bool bassAsioChannelJoin = BassAsio.BASS_ASIO_ChannelJoin(false, 1, 0);
bool bassAsioStart = BassAsio.BASS_ASIO_Start(0);
}
void WindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
{
Closing -= WindowClosing;
bool bassAsioStop = BassAsio.BASS_ASIO_Stop();
bool bassAsioFree = BassAsio.BASS_ASIO_Free();
}
private void ButtonDirectionClick(object sender, RoutedEventArgs e)
{
if (sender == null) return;
Button button = sender as Button;
if (button == null) return;
// Reverse direction
float currentDirection = 0;
bool getAttribute = Bass.BASS_ChannelGetAttribute(_pReverse, BASSAttribute.BASS_ATTRIB_REVERSE_DIR, ref currentDirection);
float newDirection = currentDirection >= 0 ? -1 : 1;
bool setAttribute = Bass.BASS_ChannelSetAttribute(_pReverse, BASSAttribute.BASS_ATTRIB_REVERSE_DIR, newDirection);
bool reset = BassAsio.BASS_ASIO_ChannelReset(false, 0, BASSASIOReset.BASS_ASIO_RESET_PAUSE);
button.Content = newDirection;
}
private void SliderTempoValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
bool setRate = BassAsio.BASS_ASIO_ChannelSetRate(false, 0, e.NewValue);
}
private void SyncProc(int handle, int channel, int data, IntPtr user)
{
if (channel == _pReverse)
{
// We pause it which we'll allow us to unpause it later
bool bassAsioChannelPause = BassAsio.BASS_ASIO_ChannelPause(false, 0);
}
}
private int AsioProc(bool input, int channel, IntPtr buffer, int length, IntPtr user)
{
return Bass.BASS_ChannelGetData(user.ToInt32(), buffer, length);
}
}
}