24 May '13 - 01:37 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: 1 ... 9 10 [11] 12
  Reply  |  Print  
Author Topic: BASSWASAPI beta  (Read 71652 times)
Silhwan
Posts: 81


« Reply #200 on: 20 Feb '12 - 20:47 »
Reply with quoteQuote

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 »
Reply with quoteQuote


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 »
Reply with quoteQuote

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#msg89297

Or 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 »
Reply with quoteQuote

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: 15270


« Reply #204 on: 23 Feb '12 - 15:21 »
Reply with quoteQuote

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 »
Reply with quoteQuote

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: 15270


« Reply #206 on: 24 Feb '12 - 16:00 »
Reply with quoteQuote

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 »
Reply with quoteQuote

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 »
Reply with quoteQuote

Hello,  Grin

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).aspx

I 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  Grin

Nadia
« Last Edit: 17 Apr '12 - 09:16 by nadia » Logged
Ian @ un4seen
Administrator
Posts: 15270


« Reply #209 on: 17 Apr '12 - 16:44 »
Reply with quoteQuote

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 Smiley
Logged
nadia
Posts: 272


« Reply #210 on: 17 Apr '12 - 20:32 »
Reply with quoteQuote

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 Smiley

Hello Ian  Grin

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  Grin

Nadia
« Last Edit: 18 Apr '12 - 08:38 by nadia » Logged
Ian @ un4seen
Administrator
Posts: 15270


« Reply #211 on: 18 Apr '12 - 15:34 »
Reply with quoteQuote

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 Smiley

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 Smiley
Logged
nadia
Posts: 272


« Reply #212 on: 18 Apr '12 - 17:17 »
Reply with quoteQuote

Hello Ian,  Grin

the last version you released should be in any case more than enough for most purposes and users.

Well done  Grin

Nadia
Logged
nadia
Posts: 272


« Reply #213 on: 19 Apr '12 - 11:38 »
Reply with quoteQuote

Hello Ian,  Grin

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  Grin

Nadia

Logged
Ian @ un4seen
Administrator
Posts: 15270


« Reply #214 on: 19 Apr '12 - 14:45 »
Reply with quoteQuote

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 »
Reply with quoteQuote

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: 15270


« Reply #216 on: 26 Apr '12 - 15:43 »
Reply with quoteQuote

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
Neal
Posts: 1


« Reply #217 on: 29 Apr '12 - 21:19 »
Reply with quoteQuote

Thanks Ian

No more crashes on device removal, and the callback works perfectly with Tuatara's SafeCallback in AutoIt
http://www.un4seen.com/forum/?topic=2737.msg17998;hl=callback;topicseen#msg17998
« Last Edit: 29 Apr '12 - 21:23 by Neal » Logged
docblop
Posts: 1


« Reply #218 on: 10 Jul '12 - 22:23 »
Reply with quoteQuote

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: 15270


« Reply #219 on: 11 Jul '12 - 13:48 »
Reply with quoteQuote

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
Pages: 1 ... 9 10 [11] 12
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines