19 Jun '13 - 12:01 *
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: Looking for Example [Delphi]: recording a stream played within bass.dll  (Read 2171 times)
Martin Kuhn
Posts: 9


« on: 23 May '12 - 05:17 »
Reply with quoteQuote

Hello
i'm looking for some Delphi example code helping me in following task:
- i want to record Audio into a Memorystream and/or save it as *.wav file
- Input for what has to be be recorded/saved should ONLY come from another bass-stream that is played from within the same program
- Recording should start once the bass-stream starts playing and should stop once the bass-stream is ending
- the stream/*.wav data that has to be saved has to run through my soundcard and be recorded from there
- would be perfect if the Audio is muted (not playing on my loudspeakers) whilst my programm is doing this

unfortunately the original RecordTest.pas example is not compiling on my Delphi-Compiler (Delphi 2010)
it tells me that there are incompatible types "integer" and "pointer"line in this line of the Callback routine
   Form1.WaveStream.Write(buffer^, length);


help please

best regards
Martin
Logged
Xire
Posts: 242


« Reply #1 on: 23 May '12 - 12:31 »
Reply with quoteQuote

Change the sample to

function RecordingCallback(Handle: HRECORD; buffer: Pointer; length: DWORD; user: Pointer): boolean; stdcall;
begin
    // Copy new buffer contents to the memory buffer
Form1.WaveStream.Write(buffer^, length);
    // Allow recording to continue
Result := True;
end;
Logged
Martin Kuhn
Posts: 9


« Reply #2 on: 23 May '12 - 14:34 »
Reply with quoteQuote

Thanks for your fast support Xire.
Record.pas example works after the change of the function Header as you posted.
...fine so far...

but, recording can only be done from an Input Channel from the soundcard (like eg from a mic)
isn't there the option to use something like a "virtual Input" for the soundcard?

as i've written already, my plan is to write some code that plays an existing stream and record it again after it has passed the sound card.
This is also called digital-audio-dubbing and a process which allows "legally autorized" copying of audio signals at least in some european countries
eg the software tunebite is using a technique like this. Would it be possible to do something like that also with bass?

br Martin
Logged
Xire
Posts: 242


« Reply #3 on: 23 May '12 - 15:11 »
Reply with quoteQuote

Yep, using BASSWASAPI. This works on Vista and later Windows, though.
Logged
Martin Kuhn
Posts: 9


« Reply #4 on: 23 May '12 - 15:51 »
Reply with quoteQuote

There is a delphi wrapper attached to the Zip file,
but what i'm really missing is some delphi examples how to use WASAPI for that job

br Martin
Logged
Martin Kuhn
Posts: 9


« Reply #5 on: 15 Jul '12 - 05:49 »
Reply with quoteQuote

Having "parked" this within my code development since somes days, now i wan't to bring this forward.

I tried out already some stuff but did not come to a solution yet.
it would be really nice to get some answers to my following questions

a) Is it possible to "ENABLE" a (disabled) Wasapi device? and how can i do that?
    (looking into my list of waspai-devices that i get using "BASS_WASAPI_GetDeviceInfo" i find several "disabled" devices
     where some of them might be interesting to use for recording)

b) Based on which flags, i should enumerate wasapi devices able for recording?
    -> like this: (ENABLED) AND (LOOPBACK) AND (INPUT) ??
    is the (TYPE) als important, or could i use any ?

c) i'd need some help with my source-code allthough i DON'T expect a ready coded solution.
   Would something like this be "roughly ok" do reach what i posted in my first post of his thread?

  - Init Bass and basswasapi
  - select a recording capable device (RecDev) from the list of available devices of my soundcard(s)
  - Init this device and also have the callback function in place
     -> BASS_WASAPI_Init(RecDev, 44100, 0, BASS_WASAPI_AUTOFORMAT, 1, 1, @RecordingCallback, nil)
  - Start the Playback of the song i want to record
    ->BASS_WASAPI_Start()
    -> WasapiStream := BASS_StreamCreateFile(False, PChar(FN), 0, 0, 0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
    //(WasapiStream is a HSTREAM)
    -> BASS_RecordSetInput(RecDev, BASS_INPUT_ON, -1);
    // also checked that WaveStream.size is initialised
    // and initialised also the WaveHdr (according RecTest.example)
    -> BASS_ChannelPlay(WasapiStream, False)

- Stop the Recording once the title was played and save as *.wav file
    -> BASS_ChannelStop(RecDev);
    -> complete the Waveheader   (according RecTest.example)
    -> PlaybackChan := BASS_StreamCreateFile(True, WaveStream.Memory, 0, WaveStream.Size, 0);
    -> WaveStream.SaveToFile('C:\Data\Test.wav');

best regards and Thx for any answer
MArtin




  
« Last Edit: 15 Jul '12 - 05:51 by Martin Kuhn » Logged
Chris
Posts: 1507


« Reply #6 on: 15 Jul '12 - 23:44 »
Reply with quoteQuote

Hello


unfortunately the original RecordTest.pas example is not compiling on my Delphi-Compiler (Delphi 2010)
it tells me that there are incompatible types "integer" and "pointer"line in this line of the Callback routine
   Form1.WaveStream.Write(buffer^, length);

@Ian there should be a typo error in the bass_record example please change it to
function RecordingCallback(Handle: HRECORD; buffer: Pointer; length:DWORD; user: Pointer): boolean; stdcall;
Chris
Logged
Martin Kuhn
Posts: 9


« Reply #7 on: 21 Jul '12 - 10:39 »
Reply with quoteQuote

Trying quite hard to improve my wasapi knowledge i started converting the RecTest C++ example into Delphi Code.

Although most things seem to work fine now, one major thing is not working yet.
-> Once having a streamrecorde  (e.g. from a mic) and stopping the recording the programm aborts with an errormessage
   (BASS Error-# is 37, requested data not found)
    once reaching the function where the outstream is written (line 207)
outstream := BASS_StreamCreateFile(True, WaveStream.Memory, 0, WaveStream.Size, 0);    //Params (write from Memory, TMemoryStream, No Offset, Size/TMemoryStream), no flags)

-> what am i doing wrong?


i think this is exactly what a coding forum like this is meant for. If i'm wrong please tell me.
(i ask this because receiving very limited replies to my questions)

best regards
Martin (Munich, germany)

* RecTest_Delphi.rar (146.1 KB - downloaded 26 times.)
Logged
Wishmaster
Posts: 124


« Reply #8 on: 22 Jul '12 - 02:37 »
Reply with quoteQuote

hi

I had some problems with the wasapi.dll to!
in my case i did not realize, I have two Radeon 7970 graphics card in my system.
which support "high definition audio" so in my case there are 12 devices and which are all disconnect.
so i have 17 devices listed under Control Panel\Sound
and yet my webcam's Device ID = 45 and my speakers Device ID = 13!
It is very confusing!!!

anyway I've put something together. maybe this will help you further
unit Bass_Recorder;

interface
uses

 Windows, Classes, SysUtils, Messages,

 Dynamic_Bass,
 Dynamic_WASAPI,
 Dynamic_BassASIO,
 Dynamic_DSHOW,
 Dynamic_Basscd,
 Dynamic_Bassenc,
 Dynamic_BassFX,
 Dynamic_Bassmix,
 Dynamic_Basswma,
 Dynamic_Tags,


 LibFlac,
 LibLame,
 LibMac,
 LibMpc,
 LibOgg,
 LibTTA,
 LibVorbis,
 LibVorbisEnc,
 LibVorbisFile,
 LibWV,

 Bass_Common;


type

 TRecorder = class
  private
   Saved8087CW     : WORD;      // FPU exceptions
   WndHandle       : HWND;
   EncoderType     : TEncoderType;

   Cur_DestName    : TUnicodeString;      (* Already opend fileName *)
   New_DestName    : TUnicodeString;      (* New Fielname *)

   WASAPI_IsInit   : Boolean;

   Channel_Stream  : DWORD;
   Channel_Mixer   : DWORD;
   Channel_WASAPI  : DWORD;
   Channel_Encoder : DWORD;


   procedure Set_DestinationFilename(Destination : TUnicodeString);
  protected
   { Protected declarations }
   procedure WndProc(var Msg: TMessage); virtual;
  public
   constructor Create();
   destructor Destroy; override;
   procedure Set_Encoder(Encoder : Integer);

   function WASAPI_Init_Device(Device : Integer; Freq : DWORD; Chann : DWORD) : Boolean;
   function WASAPI_Start() : Boolean;
   function WASAPI_Stop(Flush : Boolean) : Boolean;



   function Recorder_Start() : Boolean;
   function Recorder_Stop() : Boolean;
 end;

implementation
uses
 Bass_Tools,

 jbWinUtils,
 jbMathUtils,
 jbStrUtils,
 jbFileUtils;


const
  WM_RECORDER_UPDATE = WM_USER + 101;
  WM_RECORDER_START = 1;
  WM_RECORDER_PAUSE = 2;
  WM_RECORDER_STOP  = 3;

procedure TRecorder.WndProc(var Msg: TMessage);              // Todo
begin
  inherited;
 if Msg.Msg = WM_RECORDER_UPDATE then
  case Msg.WParam of
    WM_RECORDER_START :;
    WM_RECORDER_PAUSE :;
    WM_RECORDER_STOP  :;
  end;
end;


constructor TRecorder.Create();
begin
 inherited Create;
  (* Thread-safe AllocateHwnd *)
  WndHandle:= WMAllocateHWnd(WndProc);

  EncoderType:= enWAV;
end;

destructor TRecorder.Destroy;
begin
  WMDeallocateHWnd(WndHandle);

 inherited Destroy;
end;

(************************** Set Destination Filename **************************)

procedure TRecorder.Set_DestinationFilename(Destination : TUnicodeString);
begin
  Cur_DestName:= Destination;
end;


procedure TRecorder.Set_Encoder(Encoder : Integer);
begin
 EncoderType:= TEncoderType(Byte(Encoder));
end;


(******************************************************************************)
(*                                Wasapi Stuf.                                *)
(******************************************************************************)

function Input_Wasapi_Proc(buffer:Pointer; length:DWORD; user:Pointer): DWORD; stdcall;
var Buff : array [0..50000] of Byte;
    Data : DWORD;
begin
 with TRecorder(user) do
  begin
    BASS_StreamPutData(Channel_Stream, buffer, length);
    Data:= BASS_ChannelGetData(Channel_Mixer, @Buff, SizeOf(Buff));
   if Data = DW_ERROR then
    Data:= 0;

   Result:= data
  end;
end;


function TRecorder.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;

 try
    (* Free Device *)

  if BASS_WASAPI_SetDevice(DWORD(Device)) then
   begin
    if BASS_WASAPI_Free() then
     WASAPI_IsInit:= false;
   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, 0, 0, DevFlag, 1, 0.1, @Input_Wasapi_Proc, Pointer(Self)) then
   begin
     // error
     exit;
   end;


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


   StrFlag:= 0;
   StrFlag:= StrFlag or
             BASS_SAMPLE_FLOAT or
             BASS_STREAM_DECODE;

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

   WASAPI_IsInit:= true;
   Result:= true;
 except
 end;
end;

(******************************** Start Device ********************************)

function TRecorder.WASAPI_Start() : Boolean;
begin
  Result:= false;
 if BASS_WASAPI_IsStarted() then
  exit;

  Result:= BASS_WASAPI_Start();
 if not Result then
  begin
    //Error
  end;
end;

(******************************** Stop Device *********************************)

function TRecorder.WASAPI_Stop(Flush : Boolean) : Boolean;
begin
  Result:= BASS_WASAPI_Stop(Flush);
 if not Result then
  begin
    //Error
  end;
end;





function TRecorder.Recorder_Start() : Boolean;
var encFlag : DWORD;
begin

  case EncoderType of
    enWAV:
           begin
            WASAPI_Stop(true);
            Channel_Mixer:= BASS_Mixer_StreamCreate(44100, 2, BASS_STREAM_DECODE);
            BASS_Mixer_StreamAddChannel(Channel_Mixer, Channel_Stream, 0);

            encFlag:= 0;
            encFlag:= encFlag or
                      BASS_ENCODE_PAUSE or
                      BASS_ENCODE_PCM or
                      BASS_UNICODE;

            Channel_Encoder:= BASS_Encode_Start(Channel_Mixer, PChar('C:\......\Desktop\New folder (2)\wavetest.wav'), encFlag, nil, nil) ;



            WASAPI_Start();

            BASS_Encode_SetPaused(Channel_Encoder, false);
           end;
    enWMA: ;
    enMP3: ;
    enOGG: ;
    enFLAC: ;
    enMPC: ;
    enOFR: ;
    enWV: ;
    enMAC: ;
    enTTA: ;
    enAAC: ;
    enMP4: ;
    enSPX: ;
    enACM: ;
  end;

end;


function TRecorder.Recorder_Stop() : Boolean;
begin
  BASS_Encode_Stop(Channel_Encoder)
end;

    //EncoderType
end.


viel spass mit dem code Martin

« Last Edit: 22 Jul '12 - 02:47 by Wishmaster » Logged
Martin Kuhn
Posts: 9


« Reply #9 on: 28 Jul '12 - 05:54 »
Reply with quoteQuote

Attached you find my improved example code for WASAPI recording in Delphi.

best regards
Martin

PS: Thanks to Wishmaster helping me with the Bass.Enc-Tip!

* RecTestDelphi.zip (456.93 KB - downloaded 121 times.)
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines