recording live streams by using bass.dll library

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Janusz
Posts: 89
Joined: 18 Dec 2020, 17:47

recording live streams by using bass.dll library

23 Apr 2021, 05:22

I have big plea to The most advanced developers in this forum again. WOuld somebody of us be so kind and would send a simple code example, which will allow play and record live Internet audio stream by using bass.dll and by using downloadproc pointer? Combining autohotkey with bass.dll gives user very fast, reliable and memory efficient solution. Thanks to MR Lexicos, who is constantly improving autohotkey C source and its build in memory allocations routines, Autohotkey interpreter allocates really very little RAm but in same time, its build in memory optimisers do not slowdowns whole interpreter. Autohotkey 64 Bit version allow users to call 64 Bit bass.dll so tuning between various live streams is very fast.
COde should play a live stream and record it at The same time. I do not want to use The approach, that some APi function or Autohotkey command would simply open new Internet bandtwidth and simply download MP3 from The internet stream as A separate download. I would like to use downloadproc pointer.
I believe, that very professional developers here could show this secret publicly for all Autohotkey users.
It can be The code with Empty GUi window or with no GUI, only routines to get live strema to be play and record at The same time.
I believe, that it is possible, because Autohotkey contain very advanced multi threading support directly inside its C source code. So it should work like A charm.
Thank you very much for yours patience and for yours kind access to my plea.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: recording live streams by using bass.dll library

23 Apr 2021, 15:06

For those who aren't restricted to record a running stream using bass.dll, vlc's command-line option looks promising too (if it's still valid?): https://wiki.videolan.org/Documentation:Streaming_HowTo/Receive_and_Save_a_Stream/ . I've used it to create an AHK V(C)R.
Janusz
Posts: 89
Joined: 18 Dec 2020, 17:47

Re: recording live streams by using bass.dll library

05 Mar 2022, 04:47

Here is code created thanks to The authors of Streaming Bar player and thanks to MR Malcev, who has tried to do his best to solve this problem. Unfortunately, code do not work, because it have been very probably created only as a attempt to develop it to end with some efford, which is needed. Code do not cause error but it simply do not create .mp3 file at all. May be, that it would be necessary to inspire by using other code example.

Code: Select all

; Generated by Auto-GUI 3.0.1
#SingleInstance Force
#NoEnv
; #Warn  ; Enable warnings to assist with detecting common errors.

SetWorkingDir %A_ScriptDir%
SetBatchLines -1
proc := RegisterCallback("DownloadProc")
hStream := DllCall("bass.dll\BASS_StreamCreateURL", "Str","","UInt", 0, "UInt", A_IsUnicode ? BASS_UNICODE := 0x80000000 : 0, "Ptr", proc, "Ptr", 0, "UInt")
DownloadProc(buffer, length, user) 
{
	if !file
		file := FileOpen("afile.mp3", "w") 
	if !buffer
		file := 0 ; FileObj.Close() invoked automatically by the destructor
	else
	{
		numBytesWritten := file.RawWrite(buffer+0, length)
		if !numBytesWritten
			throw "wrote 0 bytes, there was some problem"
}


}



Gui Font, s9, Segoe UI
Gui Add, Button, gStart x3 y3 w80 h23, &Record and play

Gui Show, w620 h420, Live stream recorder
Return

Start:
DllCall("LoadLibrary", "Str", A_ScriptDir . "\bass.dll")
	DllCall("bass.dll\BASS_Free")
	if (DllCall("bass.dll\BASS_Init", "Int", -1, "UInt", 44100, "UInt", 0, "Ptr", A_ScriptHwnd, "Ptr", 0))

Hstream := DllCall("bass.dll\BASS_StreamCreateURL", "Str","http://icecast8.play.cz/cro1-128.mp3","UInt", 0, "UInt", A_IsUnicode ? BASS_UNICODE := 0x80000000 : 0, "Ptr", 0, "Ptr", 0, "UInt")
Hstreamm = BASS_StreamCreateURL("", 0, 0, MyDownloadProc, 0);

	DllCall("bass.dll\BASS_ChannelPlay", "UInt", hStream, "Int", 0)


Return

GuiEscape:
GuiClose:
    ExitApp
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: recording live streams by using bass.dll library

05 Mar 2022, 17:46

It is not interesting for me to write gui for bass.dll, but play 1 second from stream and save stream to disc as temp.mp3, You can do like this

Code: Select all

global file := FileOpen("temp.mp3", "w")
url := "http://icecast8.play.cz/cro1-128.mp3"
DllCall("LoadLibrary", "str", "bass.dll")
DllCall("bass.dll\BASS_Init", "int", -1, "uint", 44100, "uint", 0, "ptr", 0, "ptr", 0)
channel := DllCall("bass.dll\BASS_StreamCreateURL", "astr", url, "uint", 0, "uint", 0, "ptr", RegisterCallback("DownloadProc"), "ptr", 0, "uint")
DllCall("bass.dll\BASS_ChannelPlay", "uint", channel, "int", 0)
sleep 1000
DllCall("bass.dll\BASS_ChannelStop", "uint", channel, "int", 0)
DllCall("bass.dll\BASS_StreamFree", "uint", channel)
ExitApp

DownloadProc(buffer, length, user)
{
   if buffer
      file.RawWrite(buffer+0, length)
   else
      file.Close()
}
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: recording live streams by using bass.dll library

08 Mar 2022, 13:44

@malcev - Thx, very nice :thumbup:
Worked fine for me, until I had a try using bass.dll (x64) instead, which is bundled with its x32 equivalent here: http://www.un4seen.com/download.php?bass24
The script hasn't thrown an error but its output is created with 0 bytes. Any idea? :think:
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: recording live streams by using bass.dll library

08 Mar 2022, 15:52

For me it works OK.
Are You sure that bass.x64.dll You run from autohotkey 64 bit?
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: recording live streams by using bass.dll library

08 Mar 2022, 16:04

AHK 1.1.30.03 (64Bit)
..is Unicode: 1
bass24.zip
(926.22 KiB) Downloaded 96 times
taken from its x64 folder inside of this bundle (that I've downloaded from the link I've provided above). Dropped at A_ScriptDir

:?:
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: recording live streams by using bass.dll library

08 Mar 2022, 16:57

May be need to unblock dll, run as admin...
I do not see other reasons why it does not work on Your system.
Also, last version is AHK 1.1.33.10.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: recording live streams by using bass.dll library

08 Mar 2022, 17:07

I don't know bass.dll at all - is your script working with internet live streams, where the streams file isn't known, ie like recording a YT live stream similar to this: https://www.youtube.com/watch?v=pnsbCWOW6vo ?
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: recording live streams by using bass.dll library

08 Mar 2022, 17:15

You can test for each dllcall return value.
And then test for error.
In Your case:

Code: Select all

msgbox % channel := DllCall("bass.dll\BASS_StreamCreateURL", "astr", url, "uint", 0, "uint", 0, "ptr", RegisterCallback("DownloadProc"), "ptr", 0, "uint")
msgbox % DllCall("bass.dll\BASS_ErrorGetCode")
41 BASS_ERROR_FILEFORM
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: recording live streams by using bass.dll library

08 Mar 2022, 19:20

Tried playing a stream and writing an mp3 file without bass.dll:

Code: Select all

#Persistent
url := "https://ais-edge51-live365-dal02.cdnstream.com/a64478"
localFilePath := A_Desktop . "\test.mp3"

timeMs := 30000 ; 30 seconds

Player := ComObjCreate("WMPlayer.OCX.7")
Player.url := url

Whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
Events := new IWinHttpRequestEvents(Whr, Func("ReceiveData").Bind(localFilePath, Stop := [false]))
Whr.Open("GET", url, true)
Whr.SetRequestHeader("Cache-Control", "no-cache")
Whr.Send()

SetTimer, Terminate, -%timeMs%
Return

Terminate:
   Stop[1] := true
   Player := ""
   Sleep, 100
   Events := ""
   Whr := ""
ExitApp

ReceiveData(filePath, Stop, pData, length, moreDataAvailable) {
   static File := ""
   if (pData = "error") {
      MsgBox, 16, Error, % "Error number: " . length . "`nDescription: " . moreDataAvailable
      Return
   }
   if !File
      File := FileOpen(filePath, "w")
   if !Stop[1]
      File.RawWrite(pData + 0, length)
   if (Stop[1] || !moreDataAvailable)
      File.Close()
}

class IWinHttpRequestEvents
{ ; https://docs.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequestevents-interface
   __New(Whr, UserFunc) {
      this.UserFunc := UserFunc
      this._CreateInterface()
      this._ConnectInterface(Whr)
   }
      
   Status[] {
      get {
         Return this.Info.status
      }
   }
   
   _CreateInterface() {
      static Methods := [ {name: "QueryInterface"         , paramCount: 3}
                        , {name: "AddRef"                 , paramCount: 1}
                        , {name: "Release"                , paramCount: 1}
                        , {name: "OnResponseStart"        , paramCount: 3}
                        , {name: "OnResponseDataAvailable", paramCount: 2}
                        , {name: "OnResponseFinished"     , paramCount: 1}
                        , {name: "OnError"                , paramCount: 3} ]
                        
      this.SetCapacity("vtable", A_PtrSize*(Methods.Count() + 1))
      pVtable := this.GetAddress("vtable")
      this.SetCapacity("IUnknown", A_PtrSize)
      NumPut(pVtable, this.GetAddress("IUnknown"))
      
      this.Info := {refOffset: A_PtrSize * Methods.Count(), UserFunc: this.UserFunc}
      this.EventInst := new this.Events(this.Info)
      this.EventInst.Info := this.Info
      this.Callbacks := []
      for k, v in Methods {
         Callback := new this.BoundFuncCallback( ObjBindMethod(this.EventInst, v.name), v.paramCount, "Fast" )
         NumPut(Callback.addr, pVtable + A_PtrSize*(k - 1))
         this.Callbacks.Push(Callback)
      }
      NumPut(0, pVtable + this.Info.refOffset)
   }
   
   _ConnectInterface(Whr) {
   ; IConnectionPointContainer, IConnectionPoint — OCIdl.h
   ; IWinHttpRequestEvents — httprequest.idl
      static IID_IConnectionPointContainer := "{B196B284-BAB4-101A-B69C-00AA00341D07}"
           , IID_IWinHttpRequestEvents     := "{F97F4E15-B787-4212-80D1-D380CBBF982E}"
           
      pICPC := pIConnectionPointContainer := ComObjQuery(Whr, IID_IConnectionPointContainer)
      riid := CLSIDFromString(IID_IWinHttpRequestEvents, _)
      
      ; IConnectionPointContainer::FindConnectionPoint
      DllCall(NumGet(NumGet(pICPC + 0) + A_PtrSize*4), "Ptr", pICPC, "Ptr", riid, "PtrP", pIConnectionPoint)
      ObjRelease(pICPC), pICP := pIConnectionPoint
      
      ; IConnectionPoint::Advise
      DllCall(NumGet(NumGet(pICP + 0) + A_PtrSize*5), "Ptr", pICP, "Ptr", this.GetAddress("IUnknown"), "UIntP", cookie)
      this.pICP := pICP, this.cookie := cookie
   }
   
   __Delete() {
      ; IConnectionPoint::Unadvise
      DllCall(NumGet(NumGet(this.pICP + 0) + A_PtrSize*6), "Ptr", this.pICP, "UInt", this.cookie)
      ObjRelease(this.pICP)
      this.Delete("Callbacks")
      this.SetCapacity("vtable", 0), this.Delete("vtable")
      this.Delete("EventInst")
   }
   
   class Events {
      QueryInterface(pIWinHttpRequestEvents, riid, ppvObject) {
         static IID_IUnknown              := "{00000000-0000-0000-C000-000000000046}"
              , IID_IWinHttpRequestEvents := "{F97F4E15-B787-4212-80D1-D380CBBF982E}"
              , E_NOINTERFACE := 0x80004002, S_OK := 0, _, __
              , p1 := CLSIDFromString(IID_IUnknown             ,  _)
              , p2 := CLSIDFromString(IID_IWinHttpRequestEvents, __)
              
         if !( DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p1)
            || DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p2) )
         { ; if riid doesn't match IID_IUnknown nor IID_IWinHttpRequestEvents
            NumPut(0, ppvObject + 0)
            Return E_NOINTERFACE
         }
         else {
            NumPut(pIWinHttpRequestEvents, ppvObject + 0)
            DllCall(NumGet(NumGet(ppvObject + 0) + A_PtrSize), "Ptr", ppvObject)
            Return S_OK
         }
      }
      
      AddRef(pIWinHttpRequestEvents) {
         refOffset := NumGet(pIWinHttpRequestEvents + 0) + this.Info.refOffset
         NumPut(refCount := NumGet(refOffset + 0, "UInt") + 1, refOffset, "UInt")
         Return refCount
      }
      
      Release(pIWinHttpRequestEvents) {
         refOffset := NumGet(pIWinHttpRequestEvents + 0) + this.Info.refOffset
         NumPut(refCount := NumGet(refOffset + 0, "UInt") - 1, refOffset, "UInt")
         Return refCount
      }
      
      OnResponseStart(pIWinHttpRequestEvents, status, pType) {
         ; type := StrGet(pType)
         this.Info.status := status
         this.Info.start := true
      }
      
      OnResponseDataAvailable(pIWinHttpRequestEvents, ppSafeArray) {
         Critical
         pSafeArray := NumGet(ppSafeArray + 0)
         pData := NumGet(pSafeArray + 8 + A_PtrSize)
         length := NumGet(pSafeArray + 8 + A_PtrSize*2, "UInt")
         this.Info.UserFunc.Call(pData, length, true)
      }
      
      OnResponseFinished(pIWinHttpRequestEvents) {
         this.Info.UserFunc.Call(0, 0, false)
      }
      
      OnError(pIWinHttpRequestEvents, errorNumber, pErrorDescription) {
         this.Info.UserFunc.Call("error", errorNumber, StrGet(pErrorDescription))
      }
   }
   
   class BoundFuncCallback
   {
      __New(BoundFuncObj, paramCount, options := "") {
         this.pInfo := Object( {BoundObj: BoundFuncObj, paramCount: paramCount} )
         this.addr := RegisterCallback(this.__Class . "._Callback", options, paramCount, this.pInfo)
      }
      __Delete() {
         ObjRelease(this.pInfo)
         DllCall("GlobalFree", "Ptr", this.addr, "Ptr")
      }
      _Callback(Params*) {
         Info := Object(A_EventInfo), Args := []
         Loop % Info.paramCount
            Args.Push( NumGet(Params + A_PtrSize*(A_Index - 2)) )
         Return Info.BoundObj.Call(Args*)
      }
   }
}

CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return &CLSID
}
garry
Posts: 3740
Joined: 22 Dec 2013, 12:50

Re: recording live streams by using bass.dll library

09 Mar 2022, 09:47

@teadrinker thank you , tried to add a GUI , not sure if it's correct ( Start/Stop )
earlier I used to record with streamripper , url like this > http://87.98.130.255:8188
https://www.internet-radio.com/stations/country/

Code: Select all

;- recording live streams by using bass.dll library 
;- https://www.autohotkey.com/boards/viewtopic.php?f=76&t=89723
;-
#NoEnv
setworkingdir,%a_scriptdir%
Gui,1:default
Gui,1: -DPIScale 
Gui,1:Color,Black,Black
name1=TEST_GUI
wa:=A_screenwidth,ha:=A_screenHeight,xx:=100
Gui,1:Font,s12 cYellow,Lucida Console
;=============================================================
url:="https://ais-edge51-live365-dal02.cdnstream.com/a64478"
;url:="http://icecast8.play.cz/cro1-128.mp3"
;-- not OK > try streamripper to record /  https://www.internet-radio.com/stations/country/
;url:="http://87.98.130.255:8188"
;url:="https://www.internet-radio.com/servers/tools/playlistgenerator/?u=http://87.98.130.255:8188/listen.pls?sid=1&t=.m3u"
;---
localFilePath := A_Desktop . "\test.mp3"
;---
;timeMs := 30000                                     ; 30 seconds
;Player := ComObjCreate("WMPlayer.OCX.7")
;Player.url := url
;Whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
;Events := new IWinHttpRequestEvents(Whr, Func("ReceiveData").Bind(localFilePath, Stop := [false]))
;Whr.Open("GET", url, true)
;Whr.SetRequestHeader("Cache-Control", "no-cache")
;Whr.Send()
;SetTimer, Terminate, -%timeMs%
;return
;============================================================
;------------    
x:=(wa*1)/xx,y:=(ha*7)/xx,w:=(wa*8)/xx,h:=(ha*2.7)/xx 
Gui, Add,Button, x%x%   y%y%  w%w% h%h% gStart,START  
;------------    
x:=(wa*11)/xx,y:=(ha*7)/xx,w:=(wa*8)/xx,h:=(ha*2.7)/xx 
Gui, Add,Button, x%x%   y%y%  w%w% h%h% gTerminate,STOP  
;------------
x:=(wa*1)/xx,y:=(ha*1)/xx,w:=(wa*22)/xx,h:=(ha*11)/xx 
Gui, Show,x%x% y%y% w%w% h%h%,%name1%
;------------
x:=(wa*1)/xx,y:=(ha*1)/xx,w:=(wa*20)/xx,h:=(ha*5)/xx 
Gui, Add, Edit, x%x%   y%y%  w%w% h%h% vED1 -vscroll  , %url%
return
;-------------------------
Guiclose:
gosub,terminate
try
run,%localFilePath%
Exitapp
;-----------
esc::exitapp
;============================================================
;-----------------
start:
Gui,submit,nohide
Player := ComObjCreate("WMPlayer.OCX.7")
Player.url := url
Whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
Events := new IWinHttpRequestEvents(Whr, Func("ReceiveData").Bind(localFilePath, Stop := [false]))
Whr.Open("GET", url, true)
Whr.SetRequestHeader("Cache-Control", "no-cache")
Whr.Send()
return
;------------------------------------------------------------
Terminate:
   Stop[1] := true
   Player := ""
   Sleep, 100
   Events := ""
   Whr := ""
return
;-----------------------------------------------------------
;- function from user 'teadrinker'  ReceiveData/CLSIDFromString
;- https://www.autohotkey.com/boards/viewtopic.php?f=76&t=89723
;------------------------------------------------------------
;ReceiveData(filePath, Stop, pData, length, moreDataAvailable) {
;....
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: recording live streams by using bass.dll library

09 Mar 2022, 10:32

garry wrote: tried to add a GUI
Good! I'd add an ability to save previously entered URLs, and something like a dropdown list to choose one of them.
garry
Posts: 3740
Joined: 22 Dec 2013, 12:50

Re: recording live streams by using bass.dll library

10 Mar 2022, 02:59

@teadrinker thank you, added a dropdownlist
as example saves then to > A_Desktop . "\InternetRadio\ca_CANADA_OLDIES.mp3" ( depending radio-name )

Code: Select all

;- recording live streams by using bass.dll library 
;- https://www.autohotkey.com/boards/viewtopic.php?f=76&t=89723
;-
;- script from user teadrinker ( without bass.dll ) :
;- https://www.autohotkey.com/boards/viewtopic.php?p=450174&sid=b63c39a6e770ba8dc2c662f0b62ca3ec#p450174
;-
#NoEnv
setworkingdir,%a_scriptdir%
Gui,1:default
Gui,1: -DPIScale 
Gui,1:Color,Black,Black
name1=TEST_GUI
wa:=A_screenwidth,ha:=A_screenHeight,xx:=100
Gui,1:Font,s12 cYellow,Lucida Console
;-------------------------------------------
FD:=A_Desktop . "\InternetRadio"
ifnotexist,%fd%
  filecreatedir,%fd%
;-------------------------------------------
transform,s,chr,32
e5x=
(Ltrim comments join|
xx_MUSIC_POP             ,https://ais-edge51-live365-dal02.cdnstream.com/a64478
cz_MUSIC                 ,http://icecast8.play.cz/cro1-128.mp3
ca_CANADA_OLDIES         ,https://bluford.torontocast.com/proxy/fdfnbfxu/stream
de_GERMAN_NEWS           ,http://st01.dlf.de/dlf/01/128/mp3/stream.mp3
ch_SWISS_NEWS            ,http://stream.srg-ssr.ch/m/drs4news/mp3_128
%s%
%s%
%s%
)

;============================================================
x:=(wa*11)/xx,y:=(ha*1)/xx,w:=(wa*10)/xx,h:=(ha*0)/xx 
Gui,1:Add,DDL     , x%x%  y%y% w%w% vddlx gAA,%e5x%
;------------    
;x:=(wa*1)/xx,y:=(ha*11)/xx,w:=(wa*8)/xx,h:=(ha*2.7)/xx 
;Gui, Add,Button, x%x%   y%y%  w%w% h%h% gStart,START  
;------------    
x:=(wa*1)/xx,y:=(ha*11)/xx,w:=(wa*7)/xx,h:=(ha*2.7)/xx 
Gui, Add,Button, x%x%   y%y%  w%w% h%h% gTerminate,STOP  
;------------
x:=(wa*1)/xx,y:=(ha*1)/xx,w:=(wa*22)/xx,h:=(ha*16)/xx 
Gui, Show,x%x% y%y% w%w% h%h%,%name1%
;------------
x:=(wa*1)/xx,y:=(ha*4)/xx,w:=(wa*20)/xx,h:=(ha*5)/xx 
Gui, Add, Edit, x%x%   y%y%  w%w% h%h% vED1 -vscroll  ,
;-
GuiControl,1: ChooseString, ddlx,de_GERMAN_NEWS
return
;-------------------------
Guiclose:
gosub,terminate
try
run,%fd%
Exitapp
;-----------
esc::exitapp
;============================================================
aa:
Gui,1: submit, nohide
h1:=""
h2:=""
if ddlx<>
 {
   StringSplit,h, ddlx, `,
   Guicontrol,1:,ed1,%h2%
   try
     {
	 h1=%h1%
	 h2=%h2%
	 url:=h2
	 localfilepath:= fd . "\" . h1 . ".mp3"
	 goto,start
     }
 }
return 

;-----------------
start:
Gui,submit,nohide
Player := ComObjCreate("WMPlayer.OCX.7")
Player.url := url
Whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
Events := new IWinHttpRequestEvents(Whr, Func("ReceiveData").Bind(localFilePath, Stop := [false]))
Whr.Open("GET", url, true)
Whr.SetRequestHeader("Cache-Control", "no-cache")
Whr.Send()
return
;------------------------------------------------------------
Terminate:
   Stop[1] := true
   Player := ""
   Sleep, 100
   Events := ""
   Whr := ""
return
;-----------------------------------------------------------
;- function from user 'teadrinker'  ReceiveData/CLSIDFromString
;- https://www.autohotkey.com/boards/viewtopic.php?f=76&t=89723
;------------------------------------------------------------
ReceiveData(filePath, Stop, pData, length, moreDataAvailable) {
   static File := ""
   if (pData = "error") {
      MsgBox, 16, Error, % "Error number: " . length . "`nDescription: " . moreDataAvailable
      Return
   }
   if !File
      File := FileOpen(filePath, "w")
   if !Stop[1]
      File.RawWrite(pData + 0, length)
   if (Stop[1] || !moreDataAvailable)
      File.Close()
}
class IWinHttpRequestEvents
{ ; https://docs.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequestevents-interface
   __New(Whr, UserFunc) {
      this.UserFunc := UserFunc
      this._CreateInterface()
      this._ConnectInterface(Whr)
   }
      
   Status[] {
      get {
         Return this.Info.status
      }
   }
   
   _CreateInterface() {
      static Methods := [ {name: "QueryInterface"         , paramCount: 3}
                        , {name: "AddRef"                 , paramCount: 1}
                        , {name: "Release"                , paramCount: 1}
                        , {name: "OnResponseStart"        , paramCount: 3}
                        , {name: "OnResponseDataAvailable", paramCount: 2}
                        , {name: "OnResponseFinished"     , paramCount: 1}
                        , {name: "OnError"                , paramCount: 3} ]
                        
      this.SetCapacity("vtable", A_PtrSize*(Methods.Count() + 1))
      pVtable := this.GetAddress("vtable")
      this.SetCapacity("IUnknown", A_PtrSize)
      NumPut(pVtable, this.GetAddress("IUnknown"))
      
      this.Info := {refOffset: A_PtrSize * Methods.Count(), UserFunc: this.UserFunc}
      this.EventInst := new this.Events(this.Info)
      this.EventInst.Info := this.Info
      this.Callbacks := []
      for k, v in Methods {
         Callback := new this.BoundFuncCallback( ObjBindMethod(this.EventInst, v.name), v.paramCount, "Fast" )
         NumPut(Callback.addr, pVtable + A_PtrSize*(k - 1))
         this.Callbacks.Push(Callback)
      }
      NumPut(0, pVtable + this.Info.refOffset)
   }
   
   _ConnectInterface(Whr) {
   ; IConnectionPointContainer, IConnectionPoint — OCIdl.h
   ; IWinHttpRequestEvents — httprequest.idl
      static IID_IConnectionPointContainer := "{B196B284-BAB4-101A-B69C-00AA00341D07}"
           , IID_IWinHttpRequestEvents     := "{F97F4E15-B787-4212-80D1-D380CBBF982E}"
           
      pICPC := pIConnectionPointContainer := ComObjQuery(Whr, IID_IConnectionPointContainer)
      riid := CLSIDFromString(IID_IWinHttpRequestEvents, _)
      
      ; IConnectionPointContainer::FindConnectionPoint
      DllCall(NumGet(NumGet(pICPC + 0) + A_PtrSize*4), "Ptr", pICPC, "Ptr", riid, "PtrP", pIConnectionPoint)
      ObjRelease(pICPC), pICP := pIConnectionPoint
      
      ; IConnectionPoint::Advise
      DllCall(NumGet(NumGet(pICP + 0) + A_PtrSize*5), "Ptr", pICP, "Ptr", this.GetAddress("IUnknown"), "UIntP", cookie)
      this.pICP := pICP, this.cookie := cookie
   }
   
   __Delete() {
      ; IConnectionPoint::Unadvise
      DllCall(NumGet(NumGet(this.pICP + 0) + A_PtrSize*6), "Ptr", this.pICP, "UInt", this.cookie)
      ObjRelease(this.pICP)
      this.Delete("Callbacks")
      this.SetCapacity("vtable", 0), this.Delete("vtable")
      this.Delete("EventInst")
   }
   
   class Events {
      QueryInterface(pIWinHttpRequestEvents, riid, ppvObject) {
         static IID_IUnknown              := "{00000000-0000-0000-C000-000000000046}"
              , IID_IWinHttpRequestEvents := "{F97F4E15-B787-4212-80D1-D380CBBF982E}"
              , E_NOINTERFACE := 0x80004002, S_OK := 0, _, __
              , p1 := CLSIDFromString(IID_IUnknown             ,  _)
              , p2 := CLSIDFromString(IID_IWinHttpRequestEvents, __)
              
         if !( DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p1)
            || DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p2) )
         { ; if riid doesn't match IID_IUnknown nor IID_IWinHttpRequestEvents
            NumPut(0, ppvObject + 0)
            Return E_NOINTERFACE
         }
         else {
            NumPut(pIWinHttpRequestEvents, ppvObject + 0)
            DllCall(NumGet(NumGet(ppvObject + 0) + A_PtrSize), "Ptr", ppvObject)
            Return S_OK
         }
      }
      
      AddRef(pIWinHttpRequestEvents) {
         refOffset := NumGet(pIWinHttpRequestEvents + 0) + this.Info.refOffset
         NumPut(refCount := NumGet(refOffset + 0, "UInt") + 1, refOffset, "UInt")
         Return refCount
      }
      
      Release(pIWinHttpRequestEvents) {
         refOffset := NumGet(pIWinHttpRequestEvents + 0) + this.Info.refOffset
         NumPut(refCount := NumGet(refOffset + 0, "UInt") - 1, refOffset, "UInt")
         Return refCount
      }
      
      OnResponseStart(pIWinHttpRequestEvents, status, pType) {
         ; type := StrGet(pType)
         this.Info.status := status
         this.Info.start := true
      }
      
      OnResponseDataAvailable(pIWinHttpRequestEvents, ppSafeArray) {
         Critical
         pSafeArray := NumGet(ppSafeArray + 0)
         pData := NumGet(pSafeArray + 8 + A_PtrSize)
         length := NumGet(pSafeArray + 8 + A_PtrSize*2, "UInt")
         this.Info.UserFunc.Call(pData, length, true)
      }
      
      OnResponseFinished(pIWinHttpRequestEvents) {
         this.Info.UserFunc.Call(0, 0, false)
      }
      
      OnError(pIWinHttpRequestEvents, errorNumber, pErrorDescription) {
         this.Info.UserFunc.Call("error", errorNumber, StrGet(pErrorDescription))
      }
   }
   
   class BoundFuncCallback
   {
      __New(BoundFuncObj, paramCount, options := "") {
         this.pInfo := Object( {BoundObj: BoundFuncObj, paramCount: paramCount} )
         this.addr := RegisterCallback(this.__Class . "._Callback", options, paramCount, this.pInfo)
      }
      __Delete() {
         ObjRelease(this.pInfo)
         DllCall("GlobalFree", "Ptr", this.addr, "Ptr")
      }
      _Callback(Params*) {
         Info := Object(A_EventInfo), Args := []
         Loop % Info.paramCount
            Args.Push( NumGet(Params + A_PtrSize*(A_Index - 2)) )
         Return Info.BoundObj.Call(Args*)
      }
   }
}
CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return &CLSID
}
;-----------------------------------------------
;===============================================
EDIT2 : made buttons enable/disable LISTEN/RECORD/REC_STOP and see what happens , record radio as > date_name

Code: Select all

;- recording live streams by using bass.dll library 
;- https://www.autohotkey.com/boards/viewtopic.php?f=76&t=89723
;-
;- script from user teadrinker ( without bass.dll ) :
;- https://www.autohotkey.com/boards/viewtopic.php?p=450174&sid=b63c39a6e770ba8dc2c662f0b62ca3ec#p450174
;-
#NoEnv
setworkingdir,%a_scriptdir%
Gui,1:default
Gui,1: -DPIScale 
Gui,1:Color,Black,Black
name1=TEST_GUI
wa:=A_screenwidth,ha:=A_screenHeight,xx:=100
Gui,1:Font,s12 cYellow,Lucida Console
;-------------------------------------------
FD:=A_Desktop . "\InternetRadio"
ifnotexist,%fd%
  filecreatedir,%fd%
vlcx:=A_programfiles . "\VideoLAN\VLC\vlc.exe "
;-------------------------------------------
transform,s,chr,32
e5x=
(Ltrim comments join|
xx_MUSIC_POP             ,https://ais-edge51-live365-dal02.cdnstream.com/a64478
cz_MUSIC_NEWS            ,http://icecast8.play.cz/cro1-128.mp3
ca_CANADA_OLDIES         ,https://bluford.torontocast.com/proxy/fdfnbfxu/stream
de_GERMAN_NEWS           ,http://st01.dlf.de/dlf/01/128/mp3/stream.mp3
ch_SWISS_NEWS            ,http://stream.srg-ssr.ch/m/drs4news/mp3_128
ca_ABC 50s Canada1          ,https://igor.torontocast.com:1875/stream
ca_ABC 50s Canada2          ,http://igor.torontocast.com:1860/stream
ru_Аккорд радио             ,https://listen.myrh.ru/id052703
%s%
%s%
%s%
)
;---------------------------------------------
;http://opml.radiotime.com/Tune.ashx?id=s183508  > https://listen.myrh.ru/id052703

;============================================================
x:=(wa*11)/xx,y:=(ha*1)/xx,w:=(wa*10)/xx,h:=(ha*0)/xx 
Gui,1:Add,DDL     , x%x%  y%y% w%w% vddlx gAA,%e5x%
;------------    
x:=(wa*8)/xx,y:=(ha*14)/xx,w:=(wa*6)/xx,h:=(ha*2.7)/xx 
;Gui, Add,Button, x%x%   y%y%  w%w% h%h% gStart vRECSTART,REC_START  
Gui,1: Add, Progress,x%x% y%y% h%h% w%w% Disabled BackgroundRed
Gui,1: Add, Text, xp yp wp hp BackgroundTrans 0x201 +Border gStart vRECSTART,RECORD
;------------    
x:=(wa*15)/xx,y:=(ha*14)/xx,w:=(wa*6)/xx,h:=(ha*2.7)/xx 
Gui, Add,Button, x%x%   y%y%  w%w% h%h% gTerminate vRECSTOP,REC_STOP
;------------    
x:=(wa*1)/xx,y:=(ha*14)/xx,w:=(wa*6)/xx,h:=(ha*2.7)/xx 
Gui, Add,Button, x%x%   y%y%  w%w% h%h% gLISTEN vLISTEN,LISTEN
;------------
x:=(wa*1)/xx,y:=(ha*1)/xx,w:=(wa*22)/xx,h:=(ha*19)/xx 
Gui, Show,x%x% y%y% w%w% h%h%,%name1%
;------------
x:=(wa*1)/xx,y:=(ha*4)/xx,w:=(wa*20)/xx,h:=(ha*5)/xx 
Gui, Add, Edit, x%x%   y%y%  w%w% h%h% vED1 -vscroll  ,
;------------
x:=(wa*1)/xx,y:=(ha*10)/xx,w:=(wa*20)/xx 
Gui, Add, Edit, x%x%   y%y%  w%w% r1 vED2 -vscroll  ,
;------------
GuiControl,1: ChooseString, ddlx,ca_CANADA_OLDIES
GuiControl,1: Disable,recstop
gosub,aa
return
;-------------------------
Guiclose:
gosub,terminate
Process, Exist, vlc.exe
If ErrorLevel
  process,close,vlc.exe
;try
;run,%fd%                       ;- open folder
Exitapp
;-----------
esc::exitapp
;============================================================
aa:
Gui,1: submit, nohide
h1:=""
h2:=""
if ddlx<>
 {
   StringSplit,h, ddlx, `,
   Guicontrol,1:,ed1,%h2%
   Guicontrol,1:,ed2,
   h1=%h1%
   h2=%h2%
   url:=h2
   goto,listen
 }
return 
;-----------------
Listen:
Gui,1: submit, nohide
h1:=""
h2:=""
if ddlx<>
 {
   GuiControl,1:Disable,recstop
   GuiControl,1:Disable,listen
   StringSplit,h, ddlx, `,
   Guicontrol,1:,ed1,%h2%
	 h1=%h1%
	 h2=%h2%
	 try
	   {
	   ifexist,%vlcx%
         run,%vlcx% --one-instance --qt-start-minimized %h2%,,hide
	   else
         run,%h2%	   
	   Guicontrol,1:,ed2,PLAYING > %h1%
	   }
  }	   
return 
;-----------------
start:
Gui,submit,nohide
localfilepath:= fd . "\" . a_now . "_" . h1 . ".mp3"
Guicontrol,1:,ed2,
GuiControl,1: Disable,Listen
Process, Exist, vlc.exe
If ErrorLevel
  process,close,vlc.exe
Player := ComObjCreate("WMPlayer.OCX.7")
Player.url := url
Whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
Events := new IWinHttpRequestEvents(Whr, Func("ReceiveData").Bind(localFilePath, Stop := [false]))
Whr.Open("GET", url, true)
Whr.SetRequestHeader("Cache-Control", "no-cache")
Whr.Send()
GuiControl,1: Disable,recstart
GuiControl,1: Enable,recstop
Guicontrol,1:,ed2,Record > %h1%
return
;------------------------------------------------------------
Terminate:
Gui,submit,nohide
GuiControl,1: Enable,Listen
GuiControl,1: Enable,RecStart
   Stop[1] := true
   Player := ""
   Sleep, 100
   Events := ""
   Whr := ""
GuiControl,1:Disable,recstop
Guicontrol,1:,ed2,STOP_REC > %h1%
return
;-----------------------------------------------------------
;- function from user 'teadrinker'  ReceiveData/CLSIDFromString
;- https://www.autohotkey.com/boards/viewtopic.php?f=76&t=89723
;------------------------------------------------------------
ReceiveData(filePath, Stop, pData, length, moreDataAvailable) {
   static File := ""
   if (pData = "error") {
      MsgBox, 16, Error, % "Error number: " . length . "`nDescription: " . moreDataAvailable
      Return
   }
   if !File
      File := FileOpen(filePath, "w")
   if !Stop[1]
      File.RawWrite(pData + 0, length)
   if (Stop[1] || !moreDataAvailable)
      File.Close()
}
class IWinHttpRequestEvents
{ ; https://docs.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequestevents-interface
   __New(Whr, UserFunc) {
      this.UserFunc := UserFunc
      this._CreateInterface()
      this._ConnectInterface(Whr)
   }
      
   Status[] {
      get {
         Return this.Info.status
      }
   }
   
   _CreateInterface() {
      static Methods := [ {name: "QueryInterface"         , paramCount: 3}
                        , {name: "AddRef"                 , paramCount: 1}
                        , {name: "Release"                , paramCount: 1}
                        , {name: "OnResponseStart"        , paramCount: 3}
                        , {name: "OnResponseDataAvailable", paramCount: 2}
                        , {name: "OnResponseFinished"     , paramCount: 1}
                        , {name: "OnError"                , paramCount: 3} ]
                        
      this.SetCapacity("vtable", A_PtrSize*(Methods.Count() + 1))
      pVtable := this.GetAddress("vtable")
      this.SetCapacity("IUnknown", A_PtrSize)
      NumPut(pVtable, this.GetAddress("IUnknown"))
      
      this.Info := {refOffset: A_PtrSize * Methods.Count(), UserFunc: this.UserFunc}
      this.EventInst := new this.Events(this.Info)
      this.EventInst.Info := this.Info
      this.Callbacks := []
      for k, v in Methods {
         Callback := new this.BoundFuncCallback( ObjBindMethod(this.EventInst, v.name), v.paramCount, "Fast" )
         NumPut(Callback.addr, pVtable + A_PtrSize*(k - 1))
         this.Callbacks.Push(Callback)
      }
      NumPut(0, pVtable + this.Info.refOffset)
   }
   
   _ConnectInterface(Whr) {
   ; IConnectionPointContainer, IConnectionPoint — OCIdl.h
   ; IWinHttpRequestEvents — httprequest.idl
      static IID_IConnectionPointContainer := "{B196B284-BAB4-101A-B69C-00AA00341D07}"
           , IID_IWinHttpRequestEvents     := "{F97F4E15-B787-4212-80D1-D380CBBF982E}"
           
      pICPC := pIConnectionPointContainer := ComObjQuery(Whr, IID_IConnectionPointContainer)
      riid := CLSIDFromString(IID_IWinHttpRequestEvents, _)
      
      ; IConnectionPointContainer::FindConnectionPoint
      DllCall(NumGet(NumGet(pICPC + 0) + A_PtrSize*4), "Ptr", pICPC, "Ptr", riid, "PtrP", pIConnectionPoint)
      ObjRelease(pICPC), pICP := pIConnectionPoint
      
      ; IConnectionPoint::Advise
      DllCall(NumGet(NumGet(pICP + 0) + A_PtrSize*5), "Ptr", pICP, "Ptr", this.GetAddress("IUnknown"), "UIntP", cookie)
      this.pICP := pICP, this.cookie := cookie
   }
   
   __Delete() {
      ; IConnectionPoint::Unadvise
      DllCall(NumGet(NumGet(this.pICP + 0) + A_PtrSize*6), "Ptr", this.pICP, "UInt", this.cookie)
      ObjRelease(this.pICP)
      this.Delete("Callbacks")
      this.SetCapacity("vtable", 0), this.Delete("vtable")
      this.Delete("EventInst")
   }
   
   class Events {
      QueryInterface(pIWinHttpRequestEvents, riid, ppvObject) {
         static IID_IUnknown              := "{00000000-0000-0000-C000-000000000046}"
              , IID_IWinHttpRequestEvents := "{F97F4E15-B787-4212-80D1-D380CBBF982E}"
              , E_NOINTERFACE := 0x80004002, S_OK := 0, _, __
              , p1 := CLSIDFromString(IID_IUnknown             ,  _)
              , p2 := CLSIDFromString(IID_IWinHttpRequestEvents, __)
              
         if !( DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p1)
            || DllCall("Ole32\IsEqualGUID", "Ptr", riid, "Ptr", p2) )
         { ; if riid doesn't match IID_IUnknown nor IID_IWinHttpRequestEvents
            NumPut(0, ppvObject + 0)
            Return E_NOINTERFACE
         }
         else {
            NumPut(pIWinHttpRequestEvents, ppvObject + 0)
            DllCall(NumGet(NumGet(ppvObject + 0) + A_PtrSize), "Ptr", ppvObject)
            Return S_OK
         }
      }
      
      AddRef(pIWinHttpRequestEvents) {
         refOffset := NumGet(pIWinHttpRequestEvents + 0) + this.Info.refOffset
         NumPut(refCount := NumGet(refOffset + 0, "UInt") + 1, refOffset, "UInt")
         Return refCount
      }
      
      Release(pIWinHttpRequestEvents) {
         refOffset := NumGet(pIWinHttpRequestEvents + 0) + this.Info.refOffset
         NumPut(refCount := NumGet(refOffset + 0, "UInt") - 1, refOffset, "UInt")
         Return refCount
      }
      
      OnResponseStart(pIWinHttpRequestEvents, status, pType) {
         ; type := StrGet(pType)
         this.Info.status := status
         this.Info.start := true
      }
      
      OnResponseDataAvailable(pIWinHttpRequestEvents, ppSafeArray) {
         Critical
         pSafeArray := NumGet(ppSafeArray + 0)
         pData := NumGet(pSafeArray + 8 + A_PtrSize)
         length := NumGet(pSafeArray + 8 + A_PtrSize*2, "UInt")
         this.Info.UserFunc.Call(pData, length, true)
      }
      
      OnResponseFinished(pIWinHttpRequestEvents) {
         this.Info.UserFunc.Call(0, 0, false)
      }
      
      OnError(pIWinHttpRequestEvents, errorNumber, pErrorDescription) {
         this.Info.UserFunc.Call("error", errorNumber, StrGet(pErrorDescription))
      }
   }
   
   class BoundFuncCallback
   {
      __New(BoundFuncObj, paramCount, options := "") {
         this.pInfo := Object( {BoundObj: BoundFuncObj, paramCount: paramCount} )
         this.addr := RegisterCallback(this.__Class . "._Callback", options, paramCount, this.pInfo)
      }
      __Delete() {
         ObjRelease(this.pInfo)
         DllCall("GlobalFree", "Ptr", this.addr, "Ptr")
      }
      _Callback(Params*) {
         Info := Object(A_EventInfo), Args := []
         Loop % Info.paramCount
            Args.Push( NumGet(Params + A_PtrSize*(A_Index - 2)) )
         Return Info.BoundObj.Call(Args*)
      }
   }
}
CLSIDFromString(IID, ByRef CLSID) {
   VarSetCapacity(CLSID, 16, 0)
   if res := DllCall("ole32\CLSIDFromString", "WStr", IID, "Ptr", &CLSID, "UInt")
      throw Exception("CLSIDFromString failed. Error: " . Format("{:#x}", res))
   Return &CLSID
}
;-----------------------------------------------
;===============================================
garry
Posts: 3740
Joined: 22 Dec 2013, 12:50

Re: recording live streams by using bass.dll library

11 Mar 2022, 06:35

@teadrinker thank you for good scripts ( as always )
;-
example , when start record then get a new URL in xy.mp3 which works then in script ( 2nd example above ) :
;http://opml.radiotime.com/Tune.ashx?id=s183508 > https://listen.myrh.ru/id052703

( had no succes with URL's like > http://87.98.130.255:8188 )
example with STREAMRIPPER for url:="http://87.98.130.255:8188"

Code: Select all

; I use WIN11 and tried streamripper from 2009-03-01 version 1.64.6
;-
;========= example downloads for 15 seconds in folder %fd% ================================
;-https://sourceforge.net/projects/streamripper/
;-https://sourceforge.net/projects/streamripper/files/streamripper%20%28current%29/1.64.6/    2009-03-01

;- HPR1: Traditional Classic Country from Heartland Public Radio
url:="http://87.98.130.255:8188"
;-------------------------------------------
FD:=A_Desktop . "\InternetRadio"
ifnotexist,%fd%
  filecreatedir,%fd%
;-------------------------------------------
pr:="C:\Program Files (x86)\Streamripper\streamripper.exe"
;runwait,%comspec% /k "%pr%" -v   ;- version
useragent:="FreeAmp/2.x"
runwait,%comspec% /k "%pr%" %url% -d %fd% -l 15 -u %useragent%
run,%fd%                          ;- open folder 
return
;===================================================================


/*
;run,%comspec% /k "%pr%" >help.txt
;- NO SUCCESS :
;useragent:="Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
;useragent:="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"
;-------------------------------------------------------------------------------------------
Usage: streamripper URL [OPTIONS]
Opts: -h             - Print this listing
      -v             - Print version info and quit
      -a [file]      - Rip to single file, default name is timestamped
      -A             - Don't write individual tracks
      -d dir         - The destination directory
      -D pattern     - Write files using specified pattern
      -s             - Don't create a directory for each stream
      -r [[ip:]port] - Create relay server on base ip:port, default port 8000
      -R #connect    - Max connections to relay, default 1, -R 0 is no limit
      -L file        - Create a relay playlist file
      -z             - Don't scan for free ports if base port is not avail
      -p url         - Use HTTP proxy server at <url>
      -o (always|never|larger|version)    - When to write tracks in complete
      -t             - Don't overwrite tracks in incomplete
      -c             - Don't auto-reconnect
      -l seconds     - Number of seconds to run, otherwise runs forever
      -M megabytes   - Stop ripping after this many megabytes
      -q [start]     - Add sequence number to output file
      -u useragent   - Use a different UserAgent than "Streamripper"
      -w rulefile    - Parse metadata using rules in file.
      -m timeout     - Number of seconds before force-closing stalled conn
      -k count       - Leave <count> tracks in incomplete
      -T             - Truncate duplicated tracks in incomplete
      -E command     - Run external command to fetch metadata
      --quiet        - Don't print ripping status to console
      --stderr       - Print ripping status to stderr (old behavior)
      --debug        - Save debugging trace
ID3 opts (mp3/aac/nsv):  [The default behavior is adding ID3V2.3 only]
      -i                           - Don't add any ID3 tags to output file
      --with-id3v1                 - Add ID3V1 tags to output file
      --without-id3v2              - Don't add ID3V2 tags to output file
Splitpoint opts (mp3 only):
      --xs-none                    - Don't search for silence
      --xs-offset=num              - Shift relative to metadata (msec)
      --xs-padding=num:num         - Add extra to prev:next track (msec)
      --xs-search-window=num:num   - Search window relative to metadata (msec)
      --xs-silence-length=num      - Expected length of silence (msec)
      --xs2                        - Use new algorithm for silence detection
Codeset opts:
      --codeset-filesys=codeset    - Specify codeset for the file system
      --codeset-id3=codeset        - Specify codeset for id3 tags
      --codeset-metadata=codeset   - Specify codeset for metadata
      --codeset-relay=codeset      - Specify codeset for the relay stream
*/
;===================================================================================================
garry
Posts: 3740
Joined: 22 Dec 2013, 12:50

Re: recording live streams by using bass.dll library

11 Mar 2022, 08:55

@teadrinker aaargh ... thank you this works .....
garry
Posts: 3740
Joined: 22 Dec 2013, 12:50

Re: recording live streams by using bass.dll library

11 Mar 2022, 12:32

@teadrinker a question , is it possible to record a sound ( record what you hear ) ?
( example you hear something in browser and use a start and stop button ( or hotkey ) and the sound is saved to %a_desktop%\Test\%a_now%_xy.mp3 ( at least 128 kB/s ) )
teadrinker
Posts: 4309
Joined: 29 Mar 2015, 09:41
Contact:

Re: recording live streams by using bass.dll library

11 Mar 2022, 13:39

Somehow this is possible, but I currently have no experience with this.

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: dunnerca, Google [Bot], wilkster and 131 guests