|
Silhwan
Posts: 81
|
 |
« Reply #200 on: 20 Feb '12 - 20:47 » |
Quote
|
My previous posted unit(WasapiVolumeSlide.pas) for smooth mute is effective only if the device is in shared mode. Here is the revised unit which is effective on both of Exclusive mode and Shared mode. // Unit WasapiVolumeSlide // // This unit slides the volume level of WASAPI, which is equivalent to BASS_ChannelSlideAttribute // with attribute BASS_ATTRIB_VOL. // This unit uses my TAudioVolume component because modified version which uses BASS_WASAPI_Set... functions // does not operate as expected. // You can get TAudioVolume component at following url, // http://www.torry.net/quicksearchd.php?String=TAudioVolume&Title=Yes // // written by Silhwan Hyun (hyunsh@hanafos.com)
unit WasapiVolumeSlide;
interface
uses Windows, Classes, Messages, {basswasapi,} AudioVolume;
const // Flags fSlideForMute = 1; // execute extra sentences for mute fSlideForUnmute = 2; // execute extra sentences for unmute fKnotifyAtEnd = 4; // post message at the end of a slide
type TWasapiSlideThread = class(TThread) private { Private declarations } ThreadTimer : HWND;
FFlags : DWORD; FMsgReceiver : hwnd; FSlideEndMsg : hwnd; FExclusiveMode : boolean; FAudioVolume : TAudioVolume; FInitVolume : single; FFinalVolume : single; FStepValue : single; FMidVolume : single; procedure DoOnTimer;
protected procedure Execute; override;
public constructor Create(ExclusiveMode: boolean; AudioVolume: TAudioVolume; PeriodMS: DWORD; FinalVol: single; MsgReceiver: hwnd; SlideEndMsg, Flags: DWORD); destructor Destroy; override;
end;
// The main program should create an instance of TAudioVolume prior to calling this procedure. // Mute : true on mute, false on unmute // ExclusiveMode : true if device is in exclusive mode // AudioVolume : an instance of TAudioVolume // PeriodMS : the sliding period in milliseconds // MsgReceiver : handle to the message handler of main program // SlideEndMsg : message used for posting message at the end of a slide procedure WasapiSmoothMute(Mute: boolean; ExclusiveMode: boolean; AudioVolume : TAudioVolume; PeriodMS: DWORD; MsgReceiver: hwnd; SlideEndMsg: DWORD);
implementation
const MaxVolume = 1.0; // Volume range : 0 ~ 1.0 StepMs = 10; // Step up/down interval = 10ms
constructor TWasapiSlideThread.Create(ExclusiveMode: boolean; AudioVolume: TAudioVolume; PeriodMS: DWORD; FinalVol: single; MsgReceiver: hwnd; SlideEndMsg, Flags: DWORD); begin FreeOnTerminate := true; if FinalVol > MaxVolume then FFinalVolume := MaxVolume else if FFinalVolume < 0 then FFinalVolume := 0 else FFinalVolume := FinalVol;
ThreadTimer := CreateWaitableTimer(nil, false, 'ThreadTimer'); if ThreadTimer <> 0 then begin FExclusiveMode := ExclusiveMode; FAudioVolume := AudioVolume; FFlags := Flags; if (FFlags and fSlideForUnmute) = fSlideForUnmute then begin if ExclusiveMode then begin FAudioVolume.SetMasterVolume(0); // BASS_WASAPI_SetVolume(true, 0); FAudioVolume.SetMasterMute(False); end else begin FAudioVolume.SetVolume(0); // BASS_WASAPI_SetVolume(true, 0); FAudioVolume.SetMute(False); // BASS_WASAPI_SetMute(false); end; end;
if FExclusiveMode then FInitVolume := FAudioVolume.GetMasterVolume else FInitVolume := FAudioVolume.GetVolume; // FInitVolume := BASS_WASAPI_GetVolume(true); FStepValue := (FFinalVolume - FInitVolume) / (PeriodMS / StepMS); FMidVolume := FInitVolume; FMsgReceiver := MsgReceiver; FSlideEndMsg := SlideEndMsg; end;
inherited Create(false); // starts on creation end;
destructor TWasapiSlideThread.Destroy; begin
inherited Destroy; end;
procedure TWasapiSlideThread.Execute; const _SECOND = 10000000; // 100 nanosecond intervals var qwDueTime: int64; liDueTime: _LARGE_INTEGER; begin if ThreadTimer = 0 then begin if (FFlags and fKnotifyAtEnd) = fKnotifyAtEnd then PostMessage(FMsgReceiver, FSlideEndMsg, 0{=fail}, 0); exit; end;
// Create a negative 64-bit integer that will be used to // signal the timer 1/8 seconds from now. qwDueTime := -1 * (_SECOND div 8);
// Copy the relative time into a LARGE_INTEGER. liDueTime.LowPart := DWORD(qwDueTime and $FFFFFFFF); liDueTime.HighPart := longint(qwDueTime shr 32); if SetWaitableTimer(ThreadTimer, // handle to a timer object TLargeInteger(liDueTime), // when timer will become signaled StepMS, // periodic timer interval nil, // pointer to the completion routine nil, // data passed to the completion routine false{flag for resume state}) then
repeat if WaitForSingleObject(ThreadTimer, 1000{1sec}) = WAIT_OBJECT_0 then DoOnTimer else begin if (FFlags and fKnotifyAtEnd) = fKnotifyAtEnd then PostMessage(FMsgReceiver, FSlideEndMsg, 0{=fail}, 0);
Terminate; end; until Terminated; end;
procedure TWasapiSlideThread.DoOnTimer; begin if FInitVolume = FFinalVolume then FMidVolume := FFinalVolume else FMidVolume := FMidVolume + FStepValue; if FStepValue > 0 then begin if FMidVolume > FFinalVolume then FMidVolume := FFinalVolume end else if FMidVolume < FFinalVolume then FMidVolume := FFinalVolume;
if FExclusiveMode then FAudioVolume.SetMasterVolume(FMidVolume) else FAudioVolume.SetVolume(FMidVolume); // BASS_WASAPI_SetVolume(true, FMidVolume);
if FMidVolume = FFinalVolume then begin CancelWaitableTimer(ThreadTimer); CloseHandle (ThreadTimer); if (FFlags and fSlideForMute) = fSlideForMute then begin if FExclusiveMode then begin FAudioVolume.SetMasterMute(true); // BASS_WASAPI_SetMute(true); FAudioVolume.SetMasterVolume(FInitVolume); // BASS_WASAPI_SetVolume(true, FInitVolume); end else begin FAudioVolume.SetMute(true); // BASS_WASAPI_SetMute(true); FAudioVolume.SetVolume(FInitVolume); end; end; if (FFlags and fKnotifyAtEnd) = fKnotifyAtEnd then PostMessage(FMsgReceiver, FSlideEndMsg, 1{=success}, 0);
Terminate; end; end;
procedure WasapiSmoothMute(Mute: boolean; ExclusiveMode: boolean; AudioVolume: TAudioVolume; PeriodMS: DWORD; MsgReceiver: hwnd; SlideEndMsg: DWORD); var WasapiSlideThread: TWasapiSlideThread; begin if ExclusiveMode then begin if Mute = AudioVolume.IsMasterMuted then exit end else if Mute = AudioVolume.IsMuted then exit;
if Mute then WasapiSlideThread := TWasapiSlideThread.Create(ExclusiveMode, AudioVolume, PeriodMS, 0, MsgReceiver, SlideEndMsg, fSlideForMute or fKnotifyAtEnd) else if ExclusiveMode then WasapiSlideThread := TWasapiSlideThread.Create(ExclusiveMode, AudioVolume, PeriodMS, AudioVolume.GetMasterVolume, MsgReceiver, SlideEndMsg, fSlideForUnmute or fKnotifyAtEnd) else WasapiSlideThread := TWasapiSlideThread.Create(ExclusiveMode, AudioVolume, PeriodMS, AudioVolume.GetVolume, MsgReceiver, SlideEndMsg, fSlideForUnmute or fKnotifyAtEnd); end;
end.
|
|
|
|
|
Logged
|
|
|
|
|
Subbu
Posts: 40
|
 |
« Reply #201 on: 21 Feb '12 - 14:14 » |
Quote
|
Hi radio42,
i want to record audio using basswasapi in c#. pls help me how can i do?
|
|
|
|
|
Logged
|
|
|
|
|
radio42
Posts: 4012
|
 |
« Reply #202 on: 21 Feb '12 - 15:37 » |
Quote
|
Take a look to the documentation of the "BassWasapiHandler" class - this contains a sample. Or take a look here: http://www.un4seen.com/forum/?topic=11170.msg89297#msg89297Or from here: private BassWasapiHandler _wasapi; private EncoderLAME _encoder; ... // not playing anything via BASS, so don't need an update thread Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0); // setup BASS - "no sound" device Bass.BASS_Init(0, 48000, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero); ... // assign WASAPI input in shared-mode (replace -2 with your input device!) _wasapi = new BassWasapiHandler(-2, false, 48000, 2, 0f, 0f); // init and start WASAPI _wasapi.Init(); int recordStream = _wasapi.InputChannel; // double check, that the device is not muted externally if (_wasapi.DeviceMute) _wasapi.DeviceMute = false; _wasapi.Start(); ... // now you can use recordStream to setup any DSP/FX etc. _encoder = new EncoderLAME(recordStream); _encoder.InputFile = null; _encoder.OutputFile = "out.mp3"; _encoder.LAME_Bitrate = (int)EncoderLAME.BITRATE.kbps_192; _encoder.LAME_Mode = EncoderLAME.LAMEMode.Default; _encoder.LAME_TargetSampleRate = (int)EncoderLAME.SAMPLERATE.Hz_48000; _encoder.LAME_Quality = EncoderLAME.LAMEQuality.Quality; _encoder.Start(null, IntPtr.Zero, false); ... // once you are finished with your recording, call _encoder.Stop(); ... // and also don't forget to free wasapi when it's not needed anymore _wasapi.Stop(); _wasapi.Dispose(); _wasapi = null; Bass.BASS_Free();
|
|
|
|
|
Logged
|
|
|
|
|
Silhwan
Posts: 81
|
 |
« Reply #203 on: 23 Feb '12 - 12:40 » |
Quote
|
I have made a simple Delphi test program which is based on contest.c. It works well in Shared mode, but outputs noisy sound only in Exclusive mode. I could not find out the reason. unit WasapiTestUnit;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Bass, BassMix, BassWASAPI, Vcl.StdCtrls;
type TForm1 = class(TForm) OpenDialog1: TOpenDialog; btnPlay: TButton; btnClose: TButton; procedure FormCreate(Sender: TObject); procedure btnPlayClick(Sender: TObject); procedure btnCloseClick(Sender: TObject); private { Private declarations } public { Public declarations } end;
var Form1: TForm1;
implementation
{$R *.dfm}
var device: DWORD; mixer: HSTREAM;
//callback function function WasapiProc(buffer: pointer; length: dword; user: pointer): dword; stdcall; var c: dword; begin c := Bass_ChannelGetData(mixer, buffer, length); if c = DWORD(-1) then c := 0; Result := c; end;
procedure TForm1.btnPlayClick(Sender: TObject); var source: hstream; ci: BASS_CHANNELINFO; wi: BASS_WASAPI_INFO; begin if not OpenDialog1.Execute then exit;
source := BASS_StreamCreateFile(false, PChar(OpenDialog1.FileName), 0, 0, BASS_STREAM_DECODE or BASS_SAMPLE_FLOAT or BASS_UNICODE); if source = 0 then begin Showmessage('Error opening a stream file!'); exit; end;
BASS_ChannelGetInfo(source, ci); if not BASS_WASAPI_Init(device, ci.freq, ci.chans, BASS_WASAPI_AUTOFORMAT or BASS_WASAPI_BUFFER {or BASS_WASAPI_EXCLUSIVE}, 0.4, 0.05, WasapiProc, nil) then begin Showmessage('Error initializing WASAPI!'); exit; end;
// get the output details BASS_WASAPI_GetInfo(wi); // create a mixer with the same sample format (and enable GetPositionEx) mixer := BASS_Mixer_StreamCreate(wi.freq, wi.chans, BASS_SAMPLE_FLOAT or BASS_STREAM_DECODE or BASS_MIXER_POSEX); // add the source to the mixer (downmix if necessary) BASS_Mixer_StreamAddChannel(mixer, source, BASS_MIXER_DOWNMIX); // start it if not BASS_WASAPI_Start then Showmessage('Error Playing File!'); end;
procedure TForm1.btnCloseClick(Sender: TObject); begin BASS_WASAPI_Free; Bass_Free; Close; end;
procedure TForm1.FormCreate(Sender: TObject); var i, devcount: byte; a: DWORD; di: BASS_WASAPI_DEVICEINFO; begin // find the default output device and get its "mix" format device := DWORD(-1); a := 0; while BASS_WASAPI_GetDeviceInfo(a, di) do begin if ((di.flags and (BASS_DEVICE_DEFAULT or BASS_DEVICE_LOOPBACK or BASS_DEVICE_INPUT)) = BASS_DEVICE_DEFAULT) then begin device := a; break; end;
inc(a); end;
if (device = DWORD(-1)) then begin Showmessage('Can''t find an output device!'); exit; end;
BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 0); if not BASS_Init(0, di.mixfreq, 0, 0, nil) then Showmessage('Error initializing DS audio!'); end;
end.
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #204 on: 23 Feb '12 - 15:21 » |
Quote
|
To determine whether the problem is something specific to the Delphi translation, do you hear the same thing when playing the same file with the pre-compiled CONTEST.EXE example in the C\BIN directory?
|
|
|
|
|
Logged
|
|
|
|
|
Silhwan
Posts: 81
|
 |
« Reply #205 on: 23 Feb '12 - 21:28 » |
Quote
|
The pre-compiled CONTEST.EXE works well in exclusive mode. My Delphi sample program 's output attributes(sampling rate, channels, sample format) gotton by BASS_WASAPI_GetInfo are same to them of CONTEST.EXE at playing a stream file. One strange thing is Delphi sample program works well at my old system in exclusive mode. I cannot imagine what makes the difference of output sound.
|
|
|
|
« Last Edit: 23 Feb '12 - 23:39 by Silhwan »
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #206 on: 24 Feb '12 - 16:00 » |
Quote
|
That's strange. Your code above seems to be doing pretty much the same thing as the CONTEST example, so I'm not sure why it would produce noise when the CONTEST example doesn't. Perhaps a different BASSWASAPI.DLL version is being loaded? You can use BASS_WASAPI_GetVersion to check that.
|
|
|
|
|
Logged
|
|
|
|
|
Silhwan
Posts: 81
|
 |
« Reply #207 on: 25 Feb '12 - 02:31 » |
Quote
|
The problem is caused by using the old version of BASSWASAPI.DLL(v0.0.0,8). I thought I used the latest version of BASSWASAPI.DLL because I recently downloaded BASSWASAPI.ZIP and copied BASSWASAPI.DLL in it to "...Program" folder. But the excutable runs in "...Program\Debug" folder at debugging the program. Now the program works well after replacing the old BASSWASAPI.DLL with the latest(v0.0.0.14).
|
|
|
|
« Last Edit: 25 Feb '12 - 04:55 by Silhwan »
|
Logged
|
|
|
|
|
nadia
Posts: 272
|
 |
« Reply #208 on: 17 Apr '12 - 08:54 » |
Quote
|
Hello,  I'm starting to play with WASAPI and I've begun to see how to list available devices. Due to the fact that BassWasapi is still listed as a beta, may I suggest to add the flags BASS_DEVICE_UNPLUGGED and BASS_DEVICE_NOTPRESENT to the "flags" parameter of the BASS_WASAPI_DEVICEINFO structure? In this way it would be possible to discriminate when a certain device, tipically USB devices, has been installed on the system but unplugged from the PC. The difference between unplugged and not present is described on MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370823(v=vs.85).aspxI don't know how BassWasapi enumerates endpoints but, as far as I know, the IMMDeviceEnumerator::EnumAudioEndpoints function of CoreAudio APIs allows the possibility to discriminate the situation. Eventually, as an alternative, it could be also a good option, through the usage of a state mask, to avoid listing devices that are unplugged or not present. Kind Regards  Nadia
|
|
|
|
« Last Edit: 17 Apr '12 - 09:16 by nadia »
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #209 on: 17 Apr '12 - 16:44 » |
Quote
|
Yep, that could be useful information for the user to have, so an update for it is now up in the 1st post. I don't suppose it makes a big difference, as the other state can be inferred, but I couldn't really decide whether it was better to have a BASS_DEVICE_DISABLED flag (for DEVICE_STATE_DISABLED) or a BASS_DEVICE_NOTPRESENT flag (for DEVICE_STATE_NOTPRESENT). In the end I decided on BASS_DEVICE_DISABLED. Let me know if you think it was the wrong choice 
|
|
|
|
|
Logged
|
|
|
|
|
nadia
Posts: 272
|
 |
« Reply #210 on: 17 Apr '12 - 20:32 » |
Quote
|
Yep, that could be useful information for the user to have, so an update for it is now up in the 1st post. I don't suppose it makes a big difference, as the other state can be inferred, but I couldn't really decide whether it was better to have a BASS_DEVICE_DISABLED flag (for DEVICE_STATE_DISABLED) or a BASS_DEVICE_NOTPRESENT flag (for DEVICE_STATE_NOTPRESENT). In the end I decided on BASS_DEVICE_DISABLED. Let me know if you think it was the wrong choice  Hello Ian  from a design perspective in my opinion it would be better keeping the two states ("not present" and "disabled") separated because they mean two different things. As a side note, at a first glance the presence on the same mask of the BASS_DEVICE_ENABLED and BASS_DEVICE_DISABLED flags appears to be a bit misleading: may I suggest to change the original BASS_DEVICE_ENABLED flag to BASS_DEVICE_ACTIVE as seen inside the CoreAudio documentation? In this way there would be no confusion with devices disabled through the control panel. Kind Regards  Nadia
|
|
|
|
« Last Edit: 18 Apr '12 - 08:38 by nadia »
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #211 on: 18 Apr '12 - 15:34 » |
Quote
|
from a design perspective in my opinion it would be better keeping the two states ("not present" and "disabled") separated because they mean two different things.
They can be separated with the update  The BASSWASAPI flags correspond to the WASAPI states like this: BASS_DEVICE_ENABLED = DEVICE_STATE_ACTIVE BASS_DEVICE_UNPLUGGED = DEVICE_STATE_UNPLUGGED BASS_DEVICE_DISABLED = DEVICE_STATE_DISABLED none of the above = DEVICE_STATE_NOTPRESENT As a side note, at a first glance the presence on the same mask of the BASS_DEVICE_ENABLED and BASS_DEVICE_DISABLED flags appears to be a bit misleading: may I suggest to change the original BASS_DEVICE_ENABLED flag to BASS_DEVICE_ACTIVE as seen inside the CoreAudio documentation? In this way there would be no confusion with devices disabled through the control panel.
Yep, that was the one concern I had with the BASS_DEVICE_DISABLED flag too. The BASS_DEVICE_ENABLED flag originates from BASS (as do the BASS_DEVICE_DEFAULT and BASS_DEVICE_INIT flags), ie. when looking for available BASS devices you would check for the BASS_DEVICE_ENABLED flag with BASS_GetDeviceInfo, and the same thing can be done with BASS_WASAPI_GetDeviceInfo. Besides renaming BASS_DEVICE_ENABLED, I guess another option would be to rename BASS_DEVICE_DISABLED, perhaps to BASS_DEVICE_CPDISABLED (disabled in control panel). On the other hand, hopefully things already become clearer once the user looks at the BASS_WASAPI_DEVICEINFO documentation 
|
|
|
|
|
Logged
|
|
|
|
|
nadia
Posts: 272
|
 |
« Reply #212 on: 18 Apr '12 - 17:17 » |
Quote
|
Hello Ian,  the last version you released should be in any case more than enough for most purposes and users. Well done  Nadia
|
|
|
|
|
Logged
|
|
|
|
|
nadia
Posts: 272
|
 |
« Reply #213 on: 19 Apr '12 - 11:38 » |
Quote
|
Hello Ian,  I've found an issue inside the speakers.c source code, at least with the sound card of my desktop PC (Win 7, x86, Via High Definition Audio): in this code WASAPI is initialized in exclusive mode using the following code: BASS_WASAPI_Init(-1,-1,-1,BASS_WASAPI_AUTOFORMAT|BASS_WASAPI_EXCLUSIVE,0.4,0.05,WasapiProc,NULL)) this call will always fail with error -1 returned by BASS_ErrorGetCode. If I remove the BASS_WASAPI_AUTOFORMAT flag the returned error value is 6. May be that the correct call should be the following? BASS_WASAPI_Init(-1,0,0,BASS_WASAPI_AUTOFORMAT|BASS_WASAPI_EXCLUSIVE,0.4,0.05,WasapiProc,NULL)) Looking at your documentation I cannot see any reference to the fact that "freq" and "chans" parameters of BASS_WASAPI_Init could be set to -1 so it's likely that there is a small bug inside the sample. I thought it was worth to report. Kind Regards  Nadia
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #214 on: 19 Apr '12 - 14:45 » |
Quote
|
Oops! A corrected SPEAKERS example is up in the BASSWASAPI package now. Thanks for reporting the problem.
|
|
|
|
|
Logged
|
|
|
|
|
NealR
Guest
|
 |
« Reply #215 on: 26 Apr '12 - 08:10 » |
Quote
|
Hello Ian Thank you for your work on this project. I compiled a modified list.c and an AutoIt version with the BASS_WASAPI_SetNotify callback and BASS_WASAPI_GetDeviceInfo. I'm using the v0.0.0.14 x86 dll with both list.exe (VC++ 2k8 exp) and the AutoIt version (au3 compiled for x86 - x64 dll would not load in AutoIt) Both apps crash on Vista x86 and Win7 x64 (same mobo) when mic/line inputs are disconnected (with default windows drivers or realtek drivers) (fine on 1/8" plug connect, to line in, but instantly crashed on removal) Mobo is Asus with onboard AMD RealtekALC883 High Def audio Crashes with default drivers in Win7 x64 if output rapidly connected /disconnected (But no longer crashed with realtek drivers installed) Also crashes on input removal with just the BASS_WASAPI_SetNotify callback in both list.exe and AutoIt As an additional test, I polled BASS_WASAPI_GetDeviceInfo in AutoIt with 100ms sleep (no callback), but it would still crash when input disconnected. Vista x86 SP2 Fault Module Name: msvcrt.dll Fault Module Version: 7.0.6002.18005 Exception Code: c0000005 Exception Offset: 000136c6 OS Version: 6.0.6002.2.2.0.256.1 Thanks in advance for any insight you can offer #include "basswasapi.h" #include <stdio.h>
VOID CALLBACK WasapiNotifyProc(DWORD notify, DWORD device, void *user) { BASS_WASAPI_DEVICEINFO di; BASS_WASAPI_GetDeviceInfo(device,&di); printf("dev %d: %s\n\tid: %s\n\tflags:",device,di.name,di.id); if (di.flags&BASS_DEVICE_LOOPBACK) printf(" loopback"); if (di.flags&BASS_DEVICE_INPUT) printf(" input"); if (di.flags&BASS_DEVICE_ENABLED) printf(" enabled"); if (di.flags&BASS_DEVICE_DEFAULT) printf(" default"); if (di.flags&BASS_DEVICE_UNPLUGGED) printf(" unplugged"); if (di.flags&BASS_DEVICE_DISABLED) printf(" disabled"); printf(" (%d)\n",di.flags); }
void main() { BASS_WASAPI_SetNotify(WasapiNotifyProc,NULL); while (1) { Sleep(50); } }
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #216 on: 26 Apr '12 - 15:43 » |
Quote
|
Ah yes, disabling/removing the default device could result in a crash if there was no other device to take its place as default. An update to fix that is now up in the 1st post. Let me know if it still gives you any trouble.
|
|
|
|
|
Logged
|
|
|
|
|
|
|
docblop
Posts: 1
|
 |
« Reply #218 on: 10 Jul '12 - 22:23 » |
Quote
|
Hi everyone, I'm a newbie and I'm trying to implement a delay. Live audio coming in from the mic input should be delayed by a few seconds. I have successfully implemented it with Bass and the .net api, but had problems getting a good list of all the input devices. The code that works is Private delayStreamHandle As Integer Private liveStreamHandle As Integer Private myLiveProc As RECORDPROC Private myDelayProc As STREAMPROC = Nothing Private doublewritebuffer As Boolean = False Private noreadbuffer As Boolean = False Public MonBuffer As BASSBuffer = New BASSBuffer(5.0F, 44100, 2, 16)
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero, Guid.Empty) Bass.BASS_RecordInit(-1) Bass.BASS_RecordSetDevice(0) Bass.BASS_RecordSetInput(0, BASSInput.BASS_INPUT_ON, -1.0F) Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 20) Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_BUFFER, 200) myLiveProc = New RECORDPROC(AddressOf RecordProc) liveStreamHandle = Bass.BASS_RecordStart(44100, 2, BASSFlag.BASS_RECORD_PAUSE, 20, myLiveProc, IntPtr.Zero) myDelayProc = New STREAMPROC(AddressOf StreamProc) delayStreamHandle = Bass.BASS_StreamCreate(44100, 2, BASSFlag.BASS_DEFAULT, myDelayProc, IntPtr.Zero) MonBuffer.Clear() Bass.BASS_ChannelPlay(liveStreamHandle, False) 'start playing Bass.BASS_ChannelPlay(delayStreamHandle, False) 'start recording End Sub
Private Function RecordProc(handle As Integer, buffer As IntPtr, length As Integer, user As IntPtr) As Boolean MonBuffer.Write(buffer, length) Return True End Function
Private Function StreamProc(handle As Integer, buffer As IntPtr, length As Integer, user As IntPtr) As Integer Return MonBuffer.Read(buffer, length, user.ToInt32()) End Function I would like to change this using wasapi instead of bass. I've been looking at the documentation and going through the message board for days now, trying to figure this out. I've successfully populated dropdowns with the input and output channels, but I don't know how to connect input and output channels in a mixer to make this work. Here is some code that I've come up with so far (but it's not working): Private myWasLiveProc As WASAPIPROC Private myWasDelayProc As WASAPIPROC = Nothing
Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded Dim recItem As ComboBoxItem = BassInputComboBox.SelectedItem Dim playItem As ComboBoxItem = BassOutputComboBox.SelectedItem Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATEPERIOD, 0) Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero, Guid.Empty) myWasLiveProc = New WASAPIPROC(AddressOf InWasapiProc) BassWasapi.BASS_WASAPI_Init(recItem.Id, 44100, 2, BASSWASAPIInit.BASS_WASAPI_SHARED, 0, 0, myWasLiveProc, IntPtr.Zero) 'initialize WASAPI input device delayStreamHandle = Bass.BASS_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_DECODE, myDelayProc, IntPtr.Zero) 'create a stream to receive the data myWasDelayProc = New WASAPIPROC(AddressOf OutWasapiProc) BassWasapi.BASS_WASAPI_Init(playItem.Id, 44100, 2, BASSWASAPIInit.BASS_WASAPI_BUFFER, 0, 0, myWasDelayProc, IntPtr.Zero) 'initialize WASAPI output device outmixer = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT Or BASSFlag.BASS_STREAM_DECODE) 'create a mixer to feed the output device BassMix.BASS_Mixer_StreamAddChannel(outmixer, delayStreamHandle, 0) 'plug in the input stream End Sub
Private Function InWasapiProc(buffer As IntPtr, length As Integer, usr As IntPtr) As Integer Bass.BASS_StreamPutData(delayStreamHandle, buffer, length) 'feed the data to the input stream Return 1 'continue recording End Function
Private Function OutWasapiProc(buffer As IntPtr, length As Integer, usr As IntPtr) As Integer Dim c As Integer = Bass.BASS_ChannelGetData(outmixer, buffer, length) 'get data from the mixer If c < 0 Then Return 0 Else Return c End If End Function Can you guys give me a hint what I'm doing wrong? I'm sure it is something obvious. Maybe there is a good example somewhere you guys could point me to? I really appreciate any help with this. Thanks in advance.
|
|
|
|
|
Logged
|
|
|
|
|
Ian @ un4seen
Administrator
Posts: 15276
|
 |
« Reply #219 on: 11 Jul '12 - 13:48 » |
Quote
|
In both cases (with default output or WASAPI), a "push" stream could be used to implement the delay, by feeding silence to it before feeding in the sample data from the input/recording. For example, like this... stream=BASS_StreamCreate(44100, 2, 0, STREAMPROC_PUSH, 0); // create the output push stream BYTE silence[20000]; // silence buffer memset(silence, 0, sizeof(silence)); // fill it with silence (0s) delaybytes=BASS_ChannelSecounds2Bytes(stream, delaysecs); // get delay length in bytes while (delaybytes) { DWORD c=min(delaybytes, sizeof(silence)); // amount of silence needed BASS_StreamPutData(stream, silence, c); // add it to the output stream delaybytes-=c; } BASS_ChannelPlay(stream, 0); // start the output recording=BASS_RecordStart(44100, 2, 0, RecordProc, 0); // start recording
...
BOOL CALLBACK RecordProc(HRECORD handle, void *buf, DWORD len, void *user) { BASS_StreamPutData(stream, buf, len); // pass the recorded data to the push stream return TRUE; // continue recording }
When using WASAPI, the push stream would need to be made a "decoding channel" by adding the BASS_STREAM_DECODE flag to it, and the output WASAPIPROC would call BASS_ChannelGetData to get the data from that. It would also need to be floating-point. So something like this... stream=BASS_StreamCreate(44100, 2, BASS_STREAM_DECODE|BASS_SAMPLE_FLOAT, STREAMPROC_PUSH, 0); // create the push stream BYTE silence[20000]; // silence buffer memset(silence, 0, sizeof(silence)); // fill it with silence (0s) delaybytes=BASS_ChannelSecounds2Bytes(stream, delaysecs); // get delay length in bytes while (delaybytes) { DWORD c=min(delaybytes, sizeof(silence)); // amount of silence needed BASS_StreamPutData(stream, silence, c); // add it to the output stream delaybytes-=c; } BASS_WASAPI_Init(outdevoce, 44100, 2, 0, 0.5, 0, OutWasapiProc, NULL); // initialize the output WASAPI device BASS_WASAPI_Start(); // start it BASS_WASAPI_Init(indevoce, 44100, 2, 0, 0.5, 0, InWasapiProc, NULL); // initialize the input WASAPI device BASS_WASAPI_Start(); // start it
...
DWORD CALLBACK OutWasapiProc(void *buffer, DWORD length, void *user) { return BASS_ChannelGetData(stream, buffer, length); // get data from the push stream }
DWORD CALLBACK InWasapiProc(void *buffer, DWORD length, void *user) { return BASS_StreamPutData(stream, buffer, length); // add data to the push stream }
If the input and output WASAPI devices aren't going at the same rate, then a mixer can be added to resample the input data to the output rate... BASS_WASAPI_Init(outdevoce, 44100, 2, BASS_WASAPI_AUTOFORMAT, 0.5, 0, OutWasapiProc, NULL); // initialize the output WASAPI device BASS_WASAPI_INFO wi; BASS_WASAPI_GetInfo(&wi); // get info on the output mixer=BASS_Mixer_StreamCreate(wi.freq, wi.chans, BASS_SAMPLE_FLOAT|BASS_STREAM_DECODE); // create a mixer with the same format BASS_Mixer_StreamAddChannel(mixer, stream, 0); // add the push stream to the mixer BASS_WASAPI_Start(); // start it
...
DWORD CALLBACK OutWasapiProc(void *buffer, DWORD length, void *user) { return BASS_ChannelGetData(mixer, buffer, length); // get data from the mixer (which resamples data from the push stream) }
Please see the documentation for details on the aforementioned functions. Also, when using WASAPI output, you can save some resources by initializing the "no sound" device (number 0) in the BASS_Init call, rather than the default output device (number -1).
|
|
|
|
|
Logged
|
|
|
|
|