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.
recording live streams by using bass.dll library
Re: recording live streams by using bass.dll library
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.
Re: recording live streams by using bass.dll library
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
Re: recording live streams by using bass.dll library
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()
}
Re: recording live streams by using bass.dll library
@malcev - Thx, very nice
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?
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?
Re: recording live streams by using bass.dll library
For me it works OK.
Are You sure that bass.x64.dll You run from autohotkey 64 bit?
Are You sure that bass.x64.dll You run from autohotkey 64 bit?
Re: recording live streams by using bass.dll library
AHK 1.1.30.03 (64Bit)
..is Unicode: 1
taken from its x64 folder inside of this bundle (that I've downloaded from the link I've provided above). Dropped at A_ScriptDir
..is Unicode: 1
taken from its x64 folder inside of this bundle (that I've downloaded from the link I've provided above). Dropped at A_ScriptDir
Re: recording live streams by using bass.dll library
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.
I do not see other reasons why it does not work on Your system.
Also, last version is AHK 1.1.33.10.
Re: recording live streams by using bass.dll library
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 ?
Re: recording live streams by using bass.dll library
You can test for each dllcall return value.
And then test for error.
In Your case:
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
-
- Posts: 4309
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: recording live streams by using bass.dll library
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
}
Re: recording live streams by using bass.dll library
@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/
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) {
;....
-
- Posts: 4309
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: recording live streams by using bass.dll library
@teadrinker thank you, added a dropdownlist
as example saves then to > A_Desktop . "\InternetRadio\ca_CANADA_OLDIES.mp3" ( depending radio-name )
EDIT2 : made buttons enable/disable LISTEN/RECORD/REC_STOP and see what happens , record radio as > date_name
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
}
;-----------------------------------------------
;===============================================
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
}
;-----------------------------------------------
;===============================================
Re: recording live streams by using bass.dll library
@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"
;-
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
*/
;===================================================================================================
-
- Posts: 4309
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: recording live streams by using bass.dll library
@teadrinker aaargh ... thank you this works .....
Re: recording live streams by using bass.dll library
@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 ) )
( 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 ) )
-
- Posts: 4309
- Joined: 29 Mar 2015, 09:41
- Contact:
Re: recording live streams by using bass.dll library
Somehow this is possible, but I currently have no experience with this.