Author Topic: Locked audio file  (Read 764 times)

Couin

  • Posts: 58
Locked audio file
« on: 14 Nov '22 - 01:10 »
Hi friends,

Still working on Jingle Palette unofficial update.

I added a long time ago, a option to copy external (original) audio files into an Import subfolder of Audio_Sample on palette saving.

I noticed that if I modify original file (in an audio editor) and reassign it, I can play the new jingle (because played from the original file) but on saving palette, it can not be imported as well as the already imported file is locked.

Even if I clear the jingle button (this feature uses BASS_StreamFree() function) and save the palette, the file still locked (can not be replaced).

The only way to repalce an already imported file is clear the jingle button, save the palette, exit and reopen software, and then , import the new jingle file.

Is there a function in BASS, to unlock the file ? 
I can not use BASS_free() as well as I don't want to release the audio device.

If someone ( Ian ? jpf ? :-* ) has an idea  8)

If there is no solution, I'd try to rename the old file (I have already a feature to do this from Jingle Palette), import new, and delete renamed (old) file.

Thanks :)
Couin

Ian @ un4seen

  • Administrator
  • Posts: 24802
Re: Locked audio file
« Reply #1 on: 14 Nov '22 - 15:05 »
A stream's file shouldn't remain open/locked by BASS after the stream has been freed, so perhaps it's locked by something else? Please see if you can reproduce the problem with the pre-compiled PLUGINS.EXE example in the BASS package (C\BIN folder), ie. try playing a file with that and then play a different file and see if the first file is still locked. If that works fine, you could then use Process Explorer while running your app to find out what process has got the file open (press Ctrl+Shift+F and enter the filename). If it is your app that has the file open then double-check that you are successfully freeing the stream via BASS_StreamFree or BASS_ChannelFree, eg. log/check the return values to confirm that.

   https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer

jpf

  • Posts: 159
Re: Locked audio file
« Reply #2 on: 14 Nov '22 - 15:49 »
As Ian says, the file is most probably locked by your code somewhere.

I made a test here. I wasn't able to reproduce the problem.
I modified the \vb\BASStest sample file. I added the line
FileCopy CurDir & "\Edit\" & lstStream.Text, CurDir & "\" & lstStream.Text
to the Private Sub cmdStreamRemove_Click() sub. LIke this:
Code: [Select]
Private Sub cmdStreamRemove_Click()
    If (lstStream.ListIndex >= 0) Then
        Call BASS_StreamFree(lstStream.ItemData(lstStream.ListIndex))
        FileCopy CurDir & "\Edit\" & lstStream.Text, CurDir & "\" & lstStream.Text
        lstStream.RemoveItem lstStream.ListIndex
    End If
End Sub

The CurDir & "\Edit\" folder contains a copy of the original file from CurDir to be edited, so every time I remove the stream the edited copy on \Edit\ folder overwrites the original copy just played. I edited the file on \Edit\, clicked the "remove" button, and added + played the copied file without problems. I repeated the procedure a few times, never getting and error.

One thing you can try is commenting out parts of you code until the problem doesn't happen anymore. That will help you narrow the part locking the file.

Make sure you freed all of the streams on the file. You may have forgotten one or more instance opened.

If you can have all of the streams created by JP freed, then you can check that you have really freed all of them using BASS_GetConfig(BASS_CONFIG_HANDLES).


 

Couin

  • Posts: 58
Re: Locked audio file
« Reply #3 on: 15 Nov '22 - 00:57 »
Hi friends,

Thanks for answer's :)

I tried plugins.exe and even during playback, the file looks no t locked , I built an exe from plugins in vb6 folder, I can delete file while playing it too.

Also, I get the same result, jpf.

About BASS_GetConfig(BASS_CONFIG_HANDLES), it shoudl retunrs me the number of opens streams ? 
For example, for a palette of 10 jingles, BASS_GetConfig(BASS_CONFIG_HANDLES) should returns "10" ?

If yes, I definitivly have to check, because I get stranges results :
Palette of 25 jingles : get 60
Palette of 12 jingles : get 25
Palette of 1 jingle : get 12

Thanks :)

Edit : I'm  take version history and effectivly, 1 jingle = 1 count by MsgBox BASS_GetConfig(BASS_CONFIG_HANDLES)
But I get double result ("24" for 12 jingle) since the BASS_FX added. 
When I clear a jingle, MsgBox BASS_GetConfig(BASS_CONFIG_HANDLES)  dercrease of "2" .

Sub question :
Is existing a command to list all streams path ? Because when I load a new jingle, the number of handles is ok, but when I save the palette, the number of handles increases and I don't know which is this handle too much.
Thanks :)
« Last Edit: 15 Nov '22 - 03:27 by Couin »

jpf

  • Posts: 159
Re: Locked audio file
« Reply #4 on: 15 Nov '22 - 13:07 »
Is existing a command to list all streams path ?

You can get the filename of a stream from it's handle using
Dim Info as BASS_CHANNELINFO
BASS_ChannelGetInfo(handle, Info)
Filename = VBStrFromAnsiPtr(Info.filename)

But I don't think Bass has a function to retrieve a list of all the streams.
You can make one yourself, though.
The easiest way in VB6 could be to wrap BASS_StreamCreateFile into a function that would store the handle in a module level list (al collection, for instance) when the call to BASS_StreamCreateFile is successful.
Then the function retrieving the list of filenames actually used should check if each of the handles stored in the collection is valid, and if it is get the corresponding filename from the handle.

Let me know if you want me to post VB6 code to implement those functions.

For less memory use it would be useful to have a BASS_SYNC_FREE event delete each feed handle from the collection, but doing that in VB6 is tricky because VB6 collections arent thread-safe (nor are arrays, strings, or any variable).
« Last Edit: 15 Nov '22 - 13:15 by jpf »

Ian @ un4seen

  • Administrator
  • Posts: 24802
Re: Locked audio file
« Reply #5 on: 15 Nov '22 - 13:52 »
I tried plugins.exe and even during playback, the file looks no t locked , I built an exe from plugins in vb6 folder, I can delete file while playing it too.

That's true. Since BASS 2.4.14, files are opened with FILE_SHARE_DELETE mode enabled, which means the file can be deleted or renamed while BASS is using it (it won't actually be deleted until BASS closes it).

So if the file is locked by your app then I guess it must be some other code in your app, eg. perhaps you have some other code/library that's opening the file too?

About BASS_GetConfig(BASS_CONFIG_HANDLES), it shoudl retunrs me the number of opens streams ? 
For example, for a palette of 10 jingles, BASS_GetConfig(BASS_CONFIG_HANDLES) should returns "10" ?

Generally, yes. But it is possible for some streams (eg. HLS or WEBM streams) to have internal streams for decoding, which will also be counted by BASS_CONFIG_HANDLES. Those internal streams will be freed when the main stream is.

The main thing to check with BASS_CONFIG_HANDLES is that it isn't unexpectedly rising over time.

If yes, I definitivly have to check, because I get stranges results :
Palette of 25 jingles : get 60
Palette of 12 jingles : get 25
Palette of 1 jingle : get 12

Thanks :)

Edit : I'm  take version history and effectivly, 1 jingle = 1 count by MsgBox BASS_GetConfig(BASS_CONFIG_HANDLES)
But I get double result ("24" for 12 jingle) since the BASS_FX added.

If you're using BASS_FX's tempo or reverse processing then you will have a decoding channel (with BASS_STREAM_DECODE set) feeding a tempo/reverse stream, ie. 2 streams per file. Make sure you use the BASS_FX_FREESOURCE flag to have the source freed at the same time as the tempo/reverse stream.

When I clear a jingle, MsgBox BASS_GetConfig(BASS_CONFIG_HANDLES)  dercrease of "2" .

Freeing a tempo/reverse stream will indeed decrease the BASS_CONFIG_HANDLES value by (at least) 2 when the BASS_FX_FREESOURCE flag is used.

Couin

  • Posts: 58
Re: Locked audio file
« Reply #6 on: 16 Nov '22 - 01:13 »
Hi friends,

I add BASS_FX_FREESOURCE flag to BASS_FX_TempoCreate that had not it.

Clearing jingle buttons stil decreases 2 from BASS_CONFIG_HANDLES.

But I still sometimes get the same problem when I save the palette (with auto-import audio file if outside of Audio_Sample folder of the app) :

For example, I have a palette with 3 jingles, so BASS_CONFIG_HANDLES says 6.
I add a new jingle, so BASS_CONFIG_HANDLES say 8.
I save the palette and if I aks BASS_CONFIG_HANDLES, it says 9 .

If in the palette save function, I start with a loop to free all the 30 streams

Code: [Select]
For iCF = 0 To 29
Call BASS_StreamFree(Jing(iCF).Strm)
Call BASS_MusicFree(Jing(iCF).Strm)
Next iCF

BASS_CONFIG_HANDLES says 1 .

I don't understand which handle remains :(

About filenames, I tested from your code, jpf, but on the first jingle only :

Code: [Select]
Dim Info As BASS_CHANNELINFO
Call BASS_ChannelGetInfo(Jing(0).Strm, Info)
filename = VBStrFromAnsiPtr(Info.filename)
MsgBox filename

I get empty message box.

If a ask without VBStrFromAnsiPtr

Code: [Select]
Dim Info As BASS_CHANNELINFO
Call BASS_ChannelGetInfo(Jing(0).Strm, Info)
MsgBox Info.filename

I get 0.

But if I ask freq , I get well the freq (48000 or 44100 , depending of loading jingle) of the audio file.

Edit :
Oh, I already have a Info BASS_CHANNELINFO declaration, so if I just :
Code: [Select]
filename = VBStrFromAnsiPtr(Info.filename)
MsgBox filename
I no longer have "empty" message box, but only "D" displayed (event if the result of "MsgBox Info.filename" is different.

So, tried to declare another BASS_CHANNELINFO  and did this :
Code: [Select]
Dim Infotest As BASS_CHANNELINFO
Call BASS_ChannelGetInfo(Jing(0).Strm, Infotest )
filename = VBStrFromAnsiPtr(Infotest .filename)
MsgBox filename
And I get empty message box again ...

Edit again :

Tested with BassTest project, with adding a Command button and this code :
Code: [Select]
Private Sub Command1_Click()
Dim Infotest As BASS_CHANNELINFO
Dim filenametext As Variant
Call BASS_ChannelGetInfo(lstStream.ItemData(lstStream.ListIndex), Infotest)
filenametext = VBStrFromAnsiPtr(Infotest.filename)
MsgBox filenametext
End Sub
I still get only "D" ...

I understand nothing :(
Tired, eyes are burning  :o :-[
« Last Edit: 16 Nov '22 - 01:56 by Couin »

jpf

  • Posts: 159
Re: Locked audio file
« Reply #7 on: 16 Nov '22 - 05:32 »
@ Couin:

You're completely right about the code I posted. It's flawed.

Sorry for my mistake. I wrote the code straight to the "Post reply" text box and never tried it.

I assumed the pointer *filename was a pointer to an ANSI string, but it's actually a pointer to an UTF-16 unicode string. I don't remember this being mentioned in the documentation.

My assumption, even if unconscious and intuitive, not reasoned, wasn't completely unjustified. In my mind, the call to BASS_StreamCreateFile that created the stream didn't include the BASS_UNICODE flag (that's indeed so in Jingle Palette, if I remember well) meaning that the filename was ANSI, so it was natural to assume that when retrieved through BASS_CHANNELINFO it would be ANSI too. But now I realize that even in that case Bass converts it into unicode.

So instead of retrieving the filename using VBStrFromAnsiPtr we must use a function expecting a pointer to an UTF-16 string.

There may be better ways to implement such a function, but this is the one I'm using right now:
Code: [Select]
Private Declare Function lstrlenW Lib "kernel32" (ByVal lpString As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

Public Function StrFromPtr(ByVal lPtr As Long) As String
    Dim lLen As Long
    Dim abytBuf() As Byte

    'Get the length of the string at the memory location
    lLen = lstrlenW(lPtr) * 2 - 1   'Unicode string (must double the buffer size)
   
    If lLen > 0 Then
        ReDim abytBuf(lLen)
        'Copy the memory contents into a they byte buffer
        Call CopyMemory(abytBuf(0), ByVal lPtr, lLen)
        'convert and return the buffer
        StrFromPtr = abytBuf
    End If
End Function

Now, running the corrected code:

Code: [Select]
Dim Info As BASS_CHANNELINFO
Dim Filename as string
Call BASS_ChannelGetInfo(StreamHandle, Info)
Filename = StrFromPtr(Info.filename)
MsgBox Filename

the message box shows the correct filename.

Alternatively, if you only want it for debbuging you can avoid retrieving the string from the pointer; you can just pass the pointer to an unicode message box:

Code: [Select]
Private Declare Function MessageBoxW Lib "user32.dll" (ByVal hWnd As Long, ByVal lpText As Long, ByVal lpCaption As Long, ByVal wType As Long) As Long

Dim Info As BASS_CHANNELINFO
Call BASS_ChannelGetInfo(StreamHandle, Info)
MessageBoxW Me.hWnd, Info.filename, 0, 0


Ian @ un4seen

  • Administrator
  • Posts: 24802
Re: Locked audio file
« Reply #8 on: 16 Nov '22 - 17:08 »
For example, I have a palette with 3 jingles, so BASS_CONFIG_HANDLES says 6.
I add a new jingle, so BASS_CONFIG_HANDLES say 8.
I save the palette and if I aks BASS_CONFIG_HANDLES, it says 9 .

That looks like a new handle is being created somewhere in the "save the palette" process. Look for any stream creation calls in there. Also check for any recording and sample creation calls (BASS_CONFIG_HANDLES counts all HMUSIC / HRECORD / HSAMPLE / HSTREAM handles). If it's still a mystery, you could use logging to try to find out when exactly the extra handle appears, eg. log the BASS_CONFIG_HANDLES value at multiple points.

I assumed the pointer *filename was a pointer to an ANSI string, but it's actually a pointer to an UTF-16 unicode string. I don't remember this being mentioned in the documentation.

As VB6 strings are natively Unicode/UTF-16, the BASS_StreamCreateFile wrapper in BASS.BAS automatically applies the BASS_UNICODE flag. So the stream's BASS_CHANNELINFO filename string will indeed be UTF-16. In all cases, the BASS_CHANNELINFO flags can be checked to confirm what form the filename is in.

jpf

  • Posts: 159
Re: Locked audio file
« Reply #9 on: 16 Nov '22 - 17:59 »
As VB6 strings are natively Unicode/UTF-16, the BASS_StreamCreateFile wrapper in BASS.BAS automatically applies the BASS_UNICODE flag. So the stream's BASS_CHANNELINFO filename string will indeed be UTF-16. In all cases, the BASS_CHANNELINFO flags can be checked to confirm what form the filename is in.

Thanks! I overlooked the fact that BASS_StreamCreateFile is wrapped.

Couin

  • Posts: 58
Re: Locked audio file
« Reply #10 on: 17 Nov '22 - 03:44 »
Hi friends :)

Thanks for answers

@jpf : okay, thanks :) I tried your first solution (because debugging solution was giving error on "Me.hWnd").
This allows me to aks if BASS_StreamFree should clears the filename (and other infos of BASS_ChannelGetInfo ) ?
Because If a clear a jingle button (the clear function has BASS_StreamFree), I stil get the infos (filename, freq) of removed jingle.

========
Also, I'm searching the problem of locked file.
I think there is two (or more) lock status. I explain.
In the first test I did with BASSTest, Yes, I was able to delete the file in Windows. So I though the file was not locked.
I searched from which line of Jingle Palette assign function, the file appears locked (I use Lock hunter on the audio source file to check) since the program execute :
Code: [Select]
Jing(btNum).Strm = BASS_StreamCreateFile(BASSFALSE, StrPtr(Jing(btNum).Path), 0, 0, BASS_STREAM_DECODE)(where Jing(btNum).Stram is a array of handles, 1 handle per jingle button)
I exit sub before doing anything else (so not testing value to eventually BASS_MusicLoad and BASS_FX_TempoCreate).
Testyed with replacing BASS_STREAM_DECODE for 0, same result.

I say myself "I don't have the last version" but I was not convinced by this way.
I took bas and dll files from BASSTest, and same result.
I downlaoded BASS from website, same result.

Finally retested with BASSTest and Lock hunter, and get the same result (Added file is marked as locked by the app).

It probably explains with I get an& error on copy file to the "Import" folder on palette saving event with Call BASS_StreamFree(Jing(btNum).Strm) before copy.
Perhapps it is also explain the wrong amount of handles ?
The software should have only max 30 (jingle buttons) + 1 (Time announce signal) + 1 (Jingle to play after time announce) + 1 (Live stream) = 33 handles.

@Ian, I have not any recording function in the software so I d'ont thing this way causes problem.
I use BASS_StreamCreateFile for mp3, wav, aif... , BASS_MusicLoad if the file is a MOD, and that's all :) (Im' not really see the difference between Stream and Sample so I'm not using Sample).
« Last Edit: 17 Nov '22 - 04:07 by Couin »

jpf

  • Posts: 159
Re: Locked audio file
« Reply #11 on: 17 Nov '22 - 09:12 »
I tried your first solution (because debugging solution was giving error on "Me.hWnd").

I forgot to tell you to insert my piece of code in a form, not in a module. If you don't want the message box to belong to a form, then use 0 instead of Me.hWnd.

This allows me to aks if BASS_StreamFree should clears the filename (and other infos of BASS_ChannelGetInfo ) ?

After BASS_StreamFree is successfully called the stream doesn't exist anymore, so BASS_ChannelGetInfo will fail with error INVALID_HANDLE and the info data isn't copied into the Info BASS_CHANNELINFO structure. (If the structure contained data before the call, then that data will remain untouched. So the structure should be created or cleared just before calling BASS_ChannelGetInfo.)

Because If a clear a jingle button (the clear function has BASS_StreamFree), I stil get the infos (filename, freq) of removed jingle.

That means that the stream wasn't actually freed, so then the call to BASS_ChannelGetInfo will succeed and the infos will still be valid. You should verify that the call to BASS_StreamFree was successful and that it was called on the correct stream handle.

Couin

  • Posts: 58
Re: Locked audio file
« Reply #12 on: 17 Nov '22 - 12:15 »
Hi jpf,

Thanks, I just tre-tested the debug solution, looks ok now (tryed with frmMain.hWnd and also with 0, instead of Me.hWnd).

About freeing the stream, this is strange, because it I ask BASS_CONFIG_HANDLES, the count has been well decreased of 2 streams (main and FX streams).

jpf

  • Posts: 159
Re: Locked audio file
« Reply #13 on: 17 Nov '22 - 15:12 »
About freeing the stream, this is strange, because it I ask BASS_CONFIG_HANDLES, the count has been well decreased of 2 streams (main and FX streams).

If the handles count is still growing, that means that you created some streams and you didn't free them. This may be caused by orphaned handles, for instance. It's a usual mistake as I've seen in the forum, specially when you use module level variables for holding the handles. You may unwilingly be overwriting a handle to a stream that wasn't freed. To make sure you never do that, set to zero the variable holding each handle after each successful call to BASS_StreamFree, then after storing a new handle in that variable verify that it actually contains zero. Don't take it for granted. Be careful to also zero the variables holding the auxiliary tempo streams, and check for that before reusing them.

I see this is driving you crazy. Would you like me to have a look at your code?

Couin

  • Posts: 58
Re: Locked audio file
« Reply #14 on: 18 Nov '22 - 03:29 »
Hi jpf,

Thanks for your help :)
You're right, all that makes me a little crazy ahah !

About clearing a jingle and its infos remains, I added :
Call BASS_ChannelGetInfo(Jing(butMenuIdx).Strm, info(butMenuIdx))
and then, the filename no longer exists in the info array.

Plus, if I change Call for MsgBox in the streamfree command :
MsgBox BASS_StreamFree(Jing(butMenuIdx).Strm)
The displayed result is 1. So I thinkl it's equiv to TRUE, and the stream has been released well.

About decreasing of 2 counts, I mean for example, a palette with 2 jingles, count is 4 (because of FX streams), and after clearing one jingle, count is 2.

So, building a small app (based on BASSTest project) to make tests about locked file. Had to remove some code to not Avira see exe as (false) virus (Heur/Agen).

@jpf and Ian, I join VB6 project and compiled exe here.
Procedure :
- Execute CopyTest.exe
- Add the "ChtouinG.wav" file
- Open "ChtouinG.wav" with an audio editor like Goldwave or Audacity
- Make any modification you want on the audio
- Try to save the file, Goldwave will say you the file is read only and prupose you to save as "ChtouinG-1.wav" (Audacity directy export as "ChtouinG0.wav").

Is there a way to not limit loaded audio file as read only ?
Thanks :)

jpf

  • Posts: 159
Re: Locked audio file
« Reply #15 on: 18 Nov '22 - 06:25 »
Procedure :
- Execute CopyTest.exe
- Add the "ChtouinG.wav" file
- Open "ChtouinG.wav" with an audio editor like Goldwave or Audacity
- Make any modification you want on the audio
- Try to save the file, Goldwave will say you the file is read only and prupose you to save as "ChtouinG-1.wav" (Audacity directy export as "ChtouinG0.wav").

Is there a way to not limit loaded audio file as read only ?

I tried your CopyText.
Your "Procedure" to cause the error indeed will cause it since you're not freeing the stream before asking the audio editor to save it.

Since BASS 2.4.14, files are opened with FILE_SHARE_DELETE mode enabled, which means the file can be deleted or renamed while BASS is using it (it won't actually be deleted until BASS closes it).

I think it's not possible to overwrite a file that's opened by any application, not just Bass.

If you change your procedure to:
- Execute CopyTest.exe
- Add the "ChtouinG.wav" file
- Open "ChtouinG.wav" with an audio editor like Goldwave or Audacity
- Make any modification you want on the audio
- Hit the "Remove" button.
- Try to save the file, the audio editor will successfuly save the modified file.

Maybe it's possible for Bass to release its handle on the file by enabling the BASS_ASYNCFILE flag in your call to BASS_StreamCreateFile. I'm not sure what buffer size you must set with BASS_SetConfig(BASS_CONFIG_ASYNCFILE_BUFFER) for that to happen, or if it's possible at all. Maybe Ian can shed some light on this.

On *.wma files it's possible to play them while they're still being written, but you can't write to the position of the read pointer. I believe Bass can play many if not all of its supported audio formats while they're being written, but the limitation of not writing to the read pointer position probably still applies.

Since an audio editor saves the entire file, not just the modified part, I think it's impossible to achieve what you want. It may be possible to code an audio editor that would overwrite only the modified part, optionally waiting for the read pointer to move outside the region. But none of the editors I used allows this, as far as I know.

Couin

  • Posts: 58
Re: Locked audio file
« Reply #16 on: 18 Nov '22 - 14:29 »
Hi jpf :)

Yes, I already saw the note about not deleting the file until release, but in the example, I can delete the source file even without release it.
That's why I think there is (at least) 2 levels of locking file (lock for delete ad lock for modify).

I will test with BASS_ASYNCFILE  :)

Ian @ un4seen

  • Administrator
  • Posts: 24802
Re: Locked audio file
« Reply #17 on: 18 Nov '22 - 15:27 »
I think it's not possible to overwrite a file that's opened by any application, not just Bass.

It will depend on what sharing mode the application used when opening the file. Details on the sharing modes can be found here:

   https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea

BASS enables all 3 sharing modes, which means other apps can read/write/delete a file while BASS has it open. In the delete case, BASS will still be able to access the file until it's closed (when the stream is freed).

Maybe it's possible for Bass to release its handle on the file by enabling the BASS_ASYNCFILE flag in your call to BASS_StreamCreateFile. I'm not sure what buffer size you must set with BASS_SetConfig(BASS_CONFIG_ASYNCFILE_BUFFER) for that to happen, or if it's possible at all. Maybe Ian can shed some light on this.

The BASS_ASYNCFILE flag doesn't make any difference to the sharing mode or when BASS closes the file (which is still when the stream is freed). It only affects how BASS reads the file.

If closing a file immediately is wanted, that could be achieved by pre-reading the file to memory and then playing it from there (with mem=true in the BASS_StreamCreateFile call).

jpf

  • Posts: 159
Re: Locked audio file
« Reply #18 on: 18 Nov '22 - 20:31 »
@ Ian:

I think it's not possible to overwrite a file that's opened by any application, not just Bass.

It will depend on what sharing mode the application used when opening the file. Details on the sharing modes can be found here:

   https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea

BASS enables all 3 sharing modes, which means other apps can read/write/delete a file while BASS has it open. In the delete case, BASS will still be able to access the file until it's closed (when the stream is freed).

I was meaning I think it's not possible to overwrite an entire file (replace it) opened by another process even if it's shared in all 3 modes, because that would mean writing to the locked range of bytes pointed by the read pointer. I may have misunderstood this help, though:

https://learn.microsoft.com/en-us/windows/win32/fileio/locking-and-unlocking-byte-ranges-in-files

"Attempts to access a byte range that is locked by another process always fail."
except
"Byte range locks are ignored when using memory mapped files."

Maybe Bass is able to have no bytes range locked even when reading it?

@ Couin:

If closing a file immediately is wanted, that could be achieved by pre-reading the file to memory and then playing it from there (with mem=true in the BASS_StreamCreateFile call).

That can be achieved in your CopyTest project like this:
Code: [Select]
'module level buffer:
Dim FileData() As Byte

Private Sub cmdStreamAdd_Click()
    On Local Error Resume Next    ' incase Cancel is pressed

    DLG.Filter = "Streamable Files (wav/aif/mp3/mp2/mp1/ogg)|*.wav;*.aif;*.mp3;*.mp2;*.mp1;*.ogg|All Files (*.*)|*.*|"
    DLG.ShowOpen

    ' if cancel was pressed, exit the procedure
    If Err.Number = 32755 Then Exit Sub
    If StreamHandle <> 0 Then
        BASS_StreamFree (StreamHandle)
    End If
   
    ReDim FileData(FileLen(DLG.filename) - 1)
    Dim FileN As Integer
    FileN = FreeFile
    Open DLG.filename For Binary Access Read Shared As FileN
    Get #FileN, , FileData
    Close FileN
    StreamHandle = BASS_StreamCreateFile(BASSTRUE, VarPtr(FileData(0)), 0, UBound(FileData) + 1, 0)
    If StreamHandle = 0 Then
        ReDim FileData(0)
    End If
End Sub

' Free the stream resource
Private Sub cmdStreamRemove_Click()
    Call BASS_StreamFree(StreamHandle)
    ReDim FileData(0)
End Sub

I didn't add error check, but it's a must. The amount of available memory for a Win32 application is just 2 GB, so it's easy to run out of memory even with small sized audio files.


Couin

  • Posts: 58
Re: Locked audio file
« Reply #19 on: 22 Nov '22 - 15:14 »
Hi friends :)

Thanks for help !

@Ian : About mem=true, is it the BASSFALSE/BASSTRUE flag of BASS_StreamCreateFile ?
If yes, it appears the jingle not loading, perhaps it's because I try to load from it's path, I will try with jpf's code ASAP :)

About over point (remaining streams of palette save) I think I found why it does : in the 0 to 29 (jingles) loop , I call BASS_StreamFree only if jingle button has a non empty path, so of course, when I clear a button, and save, the stream was not freed .
Now I free all non playing jingles before starting the loop of saving jingles of the palette .
Looks to be ok for this point now.

Couin

  • Posts: 58
Re: Locked audio file
« Reply #20 on: 3 Dec '22 - 17:40 »
Hi friends,

I come back with remaing handles problems, I though I had resolved the problem but seems not.
In fact, I added a new feature to back and restor Jingle Palette configurration, including audio files.
And I noticed that restoring a backup does not always overwrite existing audio files of loaded palette, even with a loop to free streams before restoring.
So I pushed tests and here is what I saw, with 2 palettes of 2 jingles for each :

Case 1 :
Start JP, check number of streams, I get 4 streams for 2 jingles (because they have BASS_FX).
I can overwrite file with restoration.

Case 2 :
Start JP, check number of streams, I get 4 streams for 2 jingles (because they have BASS_FX).
I play one jingle, I still get 4 streams after playback.
I can not overwrite the played file with restoration.

Case 3 :
Start JP, check number of streams, I get 4 streams for 2 jingles (because they have BASS_FX).
I play one jingle, I still get 4 streams after playback.
I change palette, I get 5 streams.
I can not overwrite the played file with restoration.

Creating streams code is :
Code: [Select]
Jing(Index).Strm = BASS_StreamCreateFile(BASSFALSE, StrPtr(Jing(Index).Path), 0, 0, BASS_STREAM_DECODE Or BASS_STREAM_PRESCAN Or BASS_SAMPLE_FX)
'(But I get the same problem with Jing(Index).Strm = BASS_StreamCreateFile(BASSFALSE, StrPtr(Jing(Index).Path), 0, 0, BASS_STREAM_DECODE) instead )
If Jing(Index).Strm = 0 Then Jing(Index).Strm = BASS_MusicLoad(BASSFALSE, StrPtr(Jing(Index).Path), 0, 0, BASS_MUSIC_RAMP Or BASS_MUSIC_PRESCAN Or BASS_MUSIC_DECODE, 0)
[...]
If Jing(Index).Strm <> 0 Then
Call BASS_ChannelGetInfo(Jing(Index).Strm, info(Index))
BASS_ChannelUpdate Jing(Index).Strm, 0
Jing(Index).Strm = BASS_FX_TempoCreate(Jing(Index).Strm, BASS_FX_FREESOURCE)
[...]
End if

On restoring or changine palette, I free streams like this :
Code: [Select]
For I = 0 To 29
Call BASS_StreamFree(Jing(I).Strm)
Call BASS_MusicFree(Jing(I).Strm)
Next I

I don't understand what I do wrong. Can you confirm that these codes should release all streams and so allow overwrite ?

The workaround to restore files with overwriting, is directly BASS_Free before restoring, and initialize the sound device after.

Thanks :)

jpf

  • Posts: 159
Re: Locked audio file
« Reply #21 on: 4 Dec '22 - 10:25 »
I think the problem is that you're overwriting the file stream Jing(Index).Strm handle with the fx stream handle:
Code: [Select]
Jing(Index).Strm = BASS_StreamCreateFile(BASSFALSE, StrPtr(Jing(Index).Path), 0, 0, BASS_STREAM_DECODE Or BASS_STREAM_PRESCAN Or BASS_SAMPLE_FX)
[...]
Jing(Index).Strm = BASS_FX_TempoCreate(Jing(Index).Strm, BASS_FX_FREESOURCE)
End if

Then you free the FX stream instead of the file stream. So you don't free the file stream, leaving the file locked.
Code: [Select]
For I = 0 To 29
Call BASS_StreamFree(Jing(I).Strm)
[...]
Next I

You need to keep the file stream handle somewhere else to be able to free the stream later. Maybe add a StrmFile member to the Jing() type definition and store the BASS_StreamCreateFile() return value there?
Code: [Select]
Jing(Index).StrmFile = BASS_StreamCreateFile(BASSFALSE, StrPtr(Jing(Index).Path), 0, 0, BASS_STREAM_DECODE Or BASS_STREAM_PRESCAN Or BASS_SAMPLE_FX)
[...]
Jing(Index).Strm = BASS_FX_TempoCreate(Jing(Index).Strm, BASS_FX_FREESOURCE)
End if

Then free the stream like:
Code: [Select]
For I = 0 To 29
Call BASS_StreamFree(Jing(I).StrmFile)
Next I

The FX stream should be automatically freed by BASS_StreamFree, so you don't need to free it yourself.

Couin

  • Posts: 58
Re: Locked audio file
« Reply #22 on: 4 Dec '22 - 17:12 »
Hi jpf,

Thanks for your help, you support a doubt that I had, about FX handle taking the same place than CreateStreamFile (for decoding).

I adapted my code, following the BASS_FX example, where "chan" of StreamCreateFile is overwritten by FX TempoCreate.
Code: [Select]
    ' create decode channel
    chan = BASS_StreamCreateFile(BASSFALSE, StrPtr(CMD.FileName), 0, 0, BASS_STREAM_DECODE)

    ' create a new stream - decoded & resampled :)
    chan = BASS_FX_TempoCreate(chan, BASS_SAMPLE_LOOP Or BASS_FX_FREESOURCE)

So I added like you purpose, StrmFile def and changed my code to match with it, and looks ok, handles count sems to be ok even after a lot of manipulation (changing palette while playing etc etc...), and I can restore a backup and actual palette files can be overwritten (so not locked by handle).
Note that I had to StreamFree twice (StrmFile and Strm) for each jingle (For 0 to 29).

I hope that I will be really OK now  ;D

Thanks :)
Couin

jpf

  • Posts: 159
Re: Locked audio file
« Reply #23 on: 4 Dec '22 - 23:43 »
Congratulations, Couin! You did it!

However my "help" wasn't very clever; in fact I was wrong when I told you "The FX stream should be automatically freed by BASS_StreamFree". I thought the tempo stream was a DSP and it isn't, it's an independent stream. I should have read the BASS_FX documentation, and I didn't.

Now that you mention the prjTempo example, I just had a look at it and I think its use of "chan" is wrong. Give a look at cmdOpenFP_Click(). These lines:
Code: [Select]
    If (chan = 0) Then
        cmdOpenFP.Caption = "click here to open a file && play it..."
        Call Error_("Couldn't create a resampled stream!")
        Call BASS_StreamFree(chan)
        Call BASS_MusicFree(chan)
        Exit Sub
    End If
can't be right. "chan" holds a "0" ("If (chan = 0) Then") so then it tries to BASS_StreamFree(0). That call will always fail and no stream will be freed.

By the way, the documentation doesn't mention that a tempo stream should be freed, and there's no BASS_FX_TempoFree or the like entry point exposed by the dll. I guess BASS_FX relies on Bass to create and process the tempo stream and that's why BASS_StreamFree can take the tempo stream handle (otherwise it should fail with error INVALID_HANDLE).

Reading a bit further I found that BASS_FX exposes a BASS_FX_TempoGetSource function that you could have used to free the file stream instead of holding its handle in a variable. Something like this:
Code: [Select]
For I = 0 To 29
        Dim StrmFile as Long
        StrmFile = BASS_FX_TempoGetSource(Jing(I).Strm)
Call BASS_StreamFree(Jing(I).Strm) 'free the tempo stream
Call BASS_StreamFree(StrmFile) 'free the file stream
[...]
Next I

But don't do it this way! Keeping the handles independently is the right way to go. If for any reason the tempo stream isn't created or gets freed you won't be able to use BASS_FX_TempoGetSource to get the file stream handle and then you won't be able to free it.

Chris

  • Posts: 2105
Re: Locked audio file
« Reply #24 on: 5 Dec '22 - 11:53 »
Hi,
i did here a quick test with a little modified Code of the org Tempo Example.

----------------------------------------------------------------------------------------------------------------------------
Code: [Select]
function IsFileInUse(FileName: TFileName): Boolean;
var
  HFileRes: HFILE;
begin
  Result := False;
  if not FileExists(FileName) then
    Exit;
  HFileRes := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, 0);
  Result := (HFileRes = INVALID_HANDLE_VALUE);
  if not Result then
    CloseHandle(HFileRes);
end;
----------------------------------------------------------------------------------------------------------------------------

// create decode channel
  chan := BASS_StreamCreateFile(False, PChar(od.FileName), 0, 0, BASS_Unicode or BASS_STREAM_DECODE);

  if (chan <> 0) then
   // create a new stream - decoded & resampled :)
     chan := BASS_FX_TempoCreate(chan, BASS_SAMPLE_LOOP or BASS_FX_FREESOURCE);


if Bass_StreamFree(chan) then
begin
   if not IsFileInUse(lblFileName.Caption) then
     ShowMessage('File is writable');
end;

so the double FreeStream (Once the Bass_Handle, Second the Tempo Handle is not needed so far if you create the Tempostream and  you use the Bass_FX Flag BASS_FX_FREESOURCE