24 May '13 - 21:20 *
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]
  Reply  |  Print  
Author Topic: I need help with visualization of a channel.  (Read 502 times)
Wishmaster
Posts: 124


« on: 26 Nov '12 - 08:00 »
Reply with quoteQuote

hi
I need help with visualization of a channel. I use the add-on's BASSWASAPI and BASSmix
as recommended by Ian in the BASSWASAPI Documentation.
Quote
The BASSmix add-on can be used to play (or record) in otherwise unsupported sample formats, as well as playing multiple sources.

and since all channels are "Decode" channels, I can not use BASS_ChannelGetData or BASS_ChannelGetLevel
because they take data away from each other. Has anybody an idea? how to do this.

Wasapi Stuff 


function Input_Wasapi_Proc(buffer:Pointer; length:DWORD; user:Pointer): DWORD; stdcall;
var Buff : array [0..50000] of Byte;
    Data : DWORD;
begin
 with TBassRecorder(user) do
  begin
    // give the data to the mixer feeder stream
    BASS_StreamPutData(Channel_Feed, buffer, length);

    // recording mixer
    Data:= BASS_ChannelGetData(Channel_Mixer, @Buff, SizeOf(Buff));
   if Data = DW_ERROR then
    Data:= 1;  // continue recording

   Result:= data
  end;
end;


function TBassRecorder.Recorder_WASAPI_Init_Device(Device : Integer; Freq : DWORD; Chann : DWORD) : Boolean;             // ToDo (Freq, Chann)
var
 WasapiDevInfo : BASS_WASAPI_DEVICEINFO;
 WasapiInfo : BASS_WASAPI_INFO;
 DevFlag : DWORD;
 StrFlag : DWORD;
begin
  Result:= false;
 if not Bass_dll_Loaded[BASS_WASAPI_DLL] then
  exit;

  if BASS_StreamFree(Channel_Feed) then
   Channel_Feed:= 0;

 try
     (* Get Wasapi Device Info *)
   BASS_WASAPI_GetDeviceInfo(Device, WasapiDevInfo);

  If ((WasapiDevInfo.flags and BASS_DEVICE_INPUT) = BASS_DEVICE_INPUT) and
     ((WasapiDevInfo.flags and BASS_DEVICE_ENABLED) = BASS_DEVICE_ENABLED) then
   begin
    if BASS_WASAPI_SetDevice(Device) then
     begin
      BASS_WASAPI_Stop(true);
      BASS_WASAPI_Free();
     end;
   end;


   DevFlag:= 0;
   DevFlag:= DevFlag or
             BASS_WASAPI_AUTOFORMAT or      (* Automatically choose another sample format if the specified format is not supported.*)
             BASS_WASAPI_BUFFER;            (* Enable double buffering This requires the BASS "no sound" device to have been initilized, via BASS_Init. *)

  (* Initialize the device in shared mode else exclusive *)
  if not BASS_WASAPI_Init(Device, Freq, Chann, DevFlag, 1, 0.1, @Input_Wasapi_Proc, Pointer(Self)) then
   begin
     // error
     exit;
   end;

    DeviceID:= BASS_WASAPI_GetDevice();

  (* Get Wasapi Info *)
   BASS_WASAPI_GetInfo(WasapiInfo);
  (* Get Wasapi Device Info *)
   BASS_WASAPI_GetDeviceInfo(Device, WasapiDevInfo);

  (* Create Feeding channel *)
   StrFlag:= 0;

   StrFlag:= StrFlag or
             BASS_SAMPLE_FLOAT or
             BASS_STREAM_DECODE;

   Channel_Feed:= BASS_StreamCreate(WasapiInfo.freq, WasapiInfo.chans, StrFlag, STREAMPROC_PUSH, nil);
  if Channel_Feed = 0 then
   begin
    //Error
    exit;
   end;


   Result:= true;
 except
 end;
end;




Recordind stuff



        SendMessageW(WndHandle, WM_RECORDER_UPDATE, WM_RECORDER_CREATING, 0);

       (* Create Mixer Channel *)
        Flags[2]:= 0;
        Flags[2]:= Flags[2] or
                   BASS_STREAM_DECODE or
                   BASS_SAMPLE_FLOAT;
        Channel_Mixer:= BASS_Mixer_StreamCreate(FEncoderOptions.WAVOption.Samplerate,
                                                FEncoderOptions.WAVOption.Channels, Flags[2]);
       if Channel_Mixer = 0 then
        begin
          // Error
         exit;
        end;

       (* limiting the duration to X seconds *)
       if LimitTime then
        Time:= BASS_ChannelSeconds2Bytes(Channel_Mixer, LimitTimeLen)
       else
        Time:= 0;

       (* Add Source Channel to Mixer *)
        Flags[3]:= 0;
        Flags[3]:= Flags[3] or
                   BASS_MIXER_LIMIT or
                   BASS_MIXER_BUFFER;
       if not BASS_Mixer_StreamAddChannelEx(Channel_Mixer, Channel_Feed, Flags[3], 0, Time) then
        begin
          //error
         exit;
        end;


       (* Set Sync Limit time (Stop & Free) *)
       if LimitTime then
        begin
          Flags[5]:= 0;
          Flags[5]:= Flags[5] or
                     BASS_SYNC_POS or
                     BASS_SYNC_ONETIME;
         if BASS_ChannelSetSync(Channel_Mixer, Flags[5], Time, @Sync_Time_End, Pointer(Self)) = 0 then     
          begin
           // error
           exit;
          end;
        end;


       (* Create Encoder Channel *)
         Flags[4]:= 0;
       if not FEncoderOptions.WAVOption.Floating32Bit then
        begin
          (*
             Convert floating-point sample data to 8/16/24/32 bit integer.
             If the encoder does not support 32-bit floating-point sample data
          *)
         case FEncoderOptions.WAVOption.BitsPerSample of
          8  : Flags[4]:= Flags[4] or BASS_ENCODE_FP_8BIT;
          16 : Flags[4]:= Flags[4] or BASS_ENCODE_FP_16BIT;
          24 : Flags[4]:= Flags[4] or BASS_ENCODE_FP_24BIT;
          32 : Flags[4]:= Flags[4] or BASS_ENCODE_FP_32BIT;
         end;
        end;

       case FEncoderOptions.WAVOption.Header of
        (* Don't send a WAVE header to the encoder. *)
        hdrNOHEAD : Flags[4]:= Flags[4] or BASS_ENCODE_PCM or BASS_ENCODE_NOHEAD;
        (* Write plain PCM sample data, without an encoder. *)
        hdrPCM    : Flags[4]:= Flags[4] or BASS_ENCODE_PCM;
        (* Send an RF64 header to the encoder instead of a standard RIFF header, allowing more than 4GB of sample data. *)
        hdrRIF64  : Flags[4]:= Flags[4] or BASS_ENCODE_PCM or BASS_ENCODE_RF64;
       end;

        Flags[4]:= Flags[4] or
                   BASS_UNICODE or         (* cmdline is in UTF-16 form. Otherwise it is ANSI *)
                   BASS_ENCODE_LIMIT or    (* Limit the encoding rate to real-time speed, by introducing a delay when the rate is too high *)
                   BASS_ENCODE_PAUSE or    (* Start the encoder in a paused state *)
                   BASS_ENCODE_AUTOFREE;   (* Automatically free the encoder when the source channel is freed. *)

        Channel_Encoder:= BASS_Encode_Start(Channel_Mixer, PWideChar(New_Filenmae), Flags[4], nil, Pointer(Self));
       if Channel_Encoder = 0 then
        begin
         // Error
         Exit;
        end;

        SendMessageW(WndHandle, WM_RECORDER_UPDATE, WM_RECORDER_OPEN, 0);



Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #1 on: 26 Nov '12 - 14:15 »
Reply with quoteQuote

Do you want to visualise the WASAPI output? If so, you can specify the BASS_WASAPI_BUFFER flag in the BASS_WASAPI_Init call and then use BASS_WASAPI_GetData/Level in the same way that you would normally use BASS_ChannelGetData/Level. If you want to visualise an individual mixer source, you can specify the BASS_MIXER_BUFFER flag in the BASS_Mixer_StreamAddChannel call and then use BASS_Mixer_ChannelGetData/Level.
Logged
Wishmaster
Posts: 124


« Reply #2 on: 27 Nov '12 - 08:33 »
Reply with quoteQuote

@Ian I know that you do not use delphi but if you take a look at the code above
you would see that I already use BASS_MIXER_BUFFER and BASS_WASAPI_BUFFER
and it still does not work.

I can use the Mixer Channel to record the data! without problems.

maybe you can send me a demo based on basswasapi\c\rectest that uses BASS_Mixer_ChannelGetLevel

thx
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #3 on: 27 Nov '12 - 16:39 »
Reply with quoteQuote

You should replace any BASS_ChannelGetData/Level calls in your vis code with BASS_Mixer_ChannelGetData/Level (or BASS_WASAPI_GetData/Level). If you still have any trouble with it after doing that, please post that part of your code to get a better idea of what may be going wrong.
Logged
Wishmaster
Posts: 124


« Reply #4 on: 28 Nov '12 - 08:22 »
Reply with quoteQuote

hi.

I had already tried that! the confusing thing is, I get no error message when creating the channels
nor do i get an error message when plugging the source channel into the mixer
so i can record! using BASS_Encode_Start

But when i  plug the Mixer channel into the BASS_Mixer_ChannelGetLevel
it gives me the error code 5
Quote
BASS_ERROR_HANDLE handle is not plugged into a mixer. 

simplified
     BASS_WASAPI_Init

     Channel_Feed:= BASS_StreamCreate(44100, 2, BASS_SAMPLE_FLOAT or BASS_STREAM_DECODE, STREAMPROC_PUSH, nil);

     Channel_Mixer:= BASS_Mixer_StreamCreate(44100, 2, BASS_STREAM_DECODE or BASS_SAMPLE_FLOAT);
     BASS_Mixer_StreamAddChannel(Channel_Mixer, Channel_Feed, BASS_MIXER_BUFFER);


function Recorder_Get_VU(var L, R : Integer) : DWORD;
begin
   Result:= BASS_Mixer_ChannelGetLevel(Channel_Mixer);
 if Result = DW_ERROR then
  begin
//  Error('');  <<   error code 5
   L:= 0;
   R:= 0;
  end
 else
  begin
   L:= LOWORD(Result);
   R:= HIWORD(Result);
  end;
end;

Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #5 on: 28 Nov '12 - 17:14 »
Reply with quoteQuote

You need to use a mixer source (eg. "Channel_Mixer") in the BASS_Mixer_ChannelGetLevel call. If you would like to visualise the entire mix (not a single source), and you're feeding the mixer output to WASAPI, you could use BASS_WASAPI_GetData instead of BASS_Mixer_ChannelGetLevel.
Logged
Wishmaster
Posts: 124


« Reply #6 on: 2 Dec '12 - 08:26 »
Reply with quoteQuote

hi

I had already tried that as well! and it still does not work, at least in my previous code!
the problem lies inside the Input_Wasapi_Proc!
I tried to start BASS_WASAPI_Init before the mixer Channel was created.
and even i created the mixer Channel directly afterwards,
the program has stopped responding.

and i need the Input_Wasapi_Proc to run at all times even if the mixer Channel dose not exist   



i replaced the existing code inside the Input_Wasapi_Proc!

     Data:= BASS_ChannelGetData(Channel_Mixer, @Buff, SizeOf(Buff));
   if Data = DW_ERROR then
    Result:= 1
   
by this code

     Data:= BASS_ChannelGetData(Channel_Mixer, @Buff, SizeOf(Buff));
   if Data = DW_ERROR then
    Result:= 0
   else
    result:= 1;


however, by the change of the code a new problem arises.
I need to monitor the VU Level in order to Start/Pause/Stop the recording
or even create the Mixer Channel to begin with
can you give me some advice on how to achieve this?


I have another question, can someone translate the following code to Delphi
it's from thr basswasapi\c\rectest demo

DWORD CALLBACK InWasapiProc(void *buffer, DWORD length, void *user)
{
BYTE temp[50000];
int c;

while ((c=BASS_ChannelGetData(inmixer,temp,sizeof(temp)))>0)
{
// increase buffer size if needed
if ((reclen%BUFSTEP)+c>=BUFSTEP) {
recbuf=realloc(recbuf,((reclen+c)/BUFSTEP+1)*BUFSTEP);
if (!recbuf)
{
Error("Out of memory!");
MESS(10,WM_SETTEXT,0,"Record");
return 0; // stop recording
}
}
// buffer the data
memcpy(recbuf+reclen,temp,c);
reclen+=c;
}
return 1; // continue recording
}



// WASAPI output processing function
DWORD CALLBACK OutWasapiProc(void *buffer, DWORD length, void *user)
{
int c=BASS_ChannelGetData(outmixer,buffer,length);
if (c<0) { // at the end
if (!BASS_WASAPI_GetData(NULL,BASS_DATA_AVAILABLE)) // no buffered data remaining, so...
BASS_WASAPI_Stop(FALSE); // stop the output
return 0;
}
return c;
}



thx.   
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #7 on: 3 Dec '12 - 14:38 »
Reply with quoteQuote

i replaced the existing code inside the Input_Wasapi_Proc!

     Data:= BASS_ChannelGetData(Channel_Mixer, @Buff, SizeOf(Buff));
   if Data = DW_ERROR then
    Result:= 1

Is "Input_Wasapi_Proc" a WASAPIPROC function for a recording/input device? If so, why are you calling BASS_ChannelGetData there? That would usually only be used in an output device's WASAPIPROC.

Do you want to display the level of the recording? If so, you could call BASS_WASAPI_GetData/Level on the recording device in a timer. If you are using multiple WASAPI devices simultaneously, you should use BASS_WASAPI_SetDevice to set the device context prior to calling BASS_WASAPI_GetData/Level.

I need to monitor the VU Level in order to Start/Pause/Stop the recording
or even create the Mixer Channel to begin with
can you give me some advice on how to achieve this?

To just monitor the device's level, you could do the same as when recording (BASS_WASAPI_Init/etc) but discard the sample data in the WASAPIPROC, eg. don't save it to file. Another possibility is to use BASS_WASAPI_GetDeviceLevel, which doesn't require the device to be initialized.

I have another question, can someone translate the following code to Delphi
it's from thr basswasapi\c\rectest demo

DWORD CALLBACK InWasapiProc(void *buffer, DWORD length, void *user)
{
BYTE temp[50000];
int c;

while ((c=BASS_ChannelGetData(inmixer,temp,sizeof(temp)))>0)
...

I'm not a Delphi user myself, so unfortunately I can't help with that, but just to mention, you've missed out an important BASS_StreamPutData call from the code there. The RECTEST example's InWasapiProc function passes the recorded sample data to a push stream ("instream"), which is fed through a mixer ("inmixer") to resample it to the wanted rate, and the result is then added to a buffer ("recbuf").
Logged
Wishmaster
Posts: 124


« Reply #8 on: 4 Dec '12 - 08:42 »
Reply with quoteQuote

hi

Quote
Is "Input_Wasapi_Proc" a WASAPIPROC function for a recording/input device? If so, why are you calling BASS_ChannelGetData there? That would usually only be used in an output device's WASAPIPROC.

yes!, OK now i understand nothing. is that not the same what you do in your rectest demo with?
DWORD CALLBACK InWasapiProc(void *buffer, DWORD length, void *user)
  ...
  while ((c=BASS_ChannelGetData(inmixer,temp,sizeof(temp)))>0) {
  ...

Quote
Do you want to display the level of the recording?
I want to display the level of the mixer channel, because I add various DSP effects to the channel's

basically what i wanna do is, I want to create a channel apply various DSP effects to that channel's
then visual display the result. without recording the channel.

then create a second channel and apply the same DSP effects as to the first channel
but the second channel could have a different sample rate.
then setup a DSP Channel (BASS_ChannelSetDSP) on that second channel and that DSP Channel
will feed an external encoder,   like (lame_enc.dll, libFLAC.dll,...)

Quote
you've missed out an important BASS_StreamPutData call from the code there.
no i haven't! it's still in my (Delphi) code.
I just need the translation of the code that I had posted. I just try to understand your code
    while ((c=BASS_ChannelGetData(inmixer,temp,sizeof(temp)))>0) {
// increase buffer size if needed
if ((reclen%BUFSTEP)+c>=BUFSTEP) {
recbuf=realloc(recbuf,((reclen+c)/BUFSTEP+1)*BUFSTEP);
if (!recbuf) {
Error("Out of memory!");
MESS(10,WM_SETTEXT,0,"Record");
return 0; // stop recording
}
}
// buffer the data
memcpy(recbuf+reclen,temp,c);
reclen+=c;




should there be a better way then Please give me some advice

and thanks for your help!




Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #9 on: 4 Dec '12 - 15:40 »
Reply with quoteQuote

Quote
Is "Input_Wasapi_Proc" a WASAPIPROC function for a recording/input device? If so, why are you calling BASS_ChannelGetData there? That would usually only be used in an output device's WASAPIPROC.

yes!, OK now i understand nothing. is that not the same what you do in your rectest demo with?

OK. The RECTEST example does indeed use BASS_ChannelGetData in an input WASAPIPROC, to resample the data as described in my last reply. It wasn't clear to me that that was what you were doing too.

I want to display the level of the mixer channel, because I add various DSP effects to the channel's

In that case, you could call BASS_WASAPI_GetData/Level on the output device rather than the input device (use BASS_WASAPI_SetDevice to set the device context).

I just need the translation of the code that I had posted. I just try to understand your code
    while ((c=BASS_ChannelGetData(inmixer,temp,sizeof(temp)))>0) {
// increase buffer size if needed
if ((reclen%BUFSTEP)+c>=BUFSTEP) {
recbuf=realloc(recbuf,((reclen+c)/BUFSTEP+1)*BUFSTEP);
if (!recbuf) {
Error("Out of memory!");
MESS(10,WM_SETTEXT,0,"Record");
return 0; // stop recording
}
}
// buffer the data
memcpy(recbuf+reclen,temp,c);
reclen+=c;

The BASS_ChannelGetData call is getting the resampled data from the mixer, and the remaining code is adding that data to a buffer. If you want to do something else with the data, you can replace the buffering stuff...

    while ((c=BASS_ChannelGetData(inmixer,temp,sizeof(temp)))>0) {
        // do something with the "c" bytes of data in "temp" here
    }
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines