Extern function definition (BASS-Lib Call)

BlitzMax Forums/BlitzMax Programming/Extern function definition (BASS-Lib Call)

maverick69(Posted 2008) [#1]
Hey folks,

I'm trying to use to stream a mp3-file using BASS. I've created some simple function definitions for the bass.dll. Playing a local MP3 file works fine, but I can't get the right definition for the StreamCreateURL command.

I get a compile error like this:

Building main
Compiling:main.bmx
Linking:main.debug
Undefined symbols:
"_BASS_StreamCreateUrl", referenced from:
_62 in main.bmx.gui.debug.macos.x86.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
Build Error: Failed to link /Users/jochenheizmann/Projekte/Replay/main.debug.app/Contents/MacOS/main.debug
Process complete


The definition in the C-Header file looks like this:
#define CALLBACK
typedef uint32_t DWORD;
typedef DWORD HSTREAM;		// sample stream handle
....
#ifndef BASSDEF
  #define BASSDEF(f) WINAPI f
#endif
.....
typedef void (CALLBACK DOWNLOADPROC)(const void *buffer, DWORD length, void *user);
....
HSTREAM BASSDEF(BASS_StreamCreateURL)(const char *url, DWORD offset, DWORD flags, DOWNLOADPROC *proc, void *user);


My BlitzMax-Wrapper-Function-Definition looks like that
Extern
  Function BASS_StreamCreateUrl:Int(filename:Byte Ptr, offset:Int, flags:Int, callback:Byte Ptr, user:Int)


What I am doing wrong???

P.S.: I also tried Brucys Bass.Mod but the streaming functions are not yet implmented :(


Brucey(Posted 2008) [#2]
I also tried Brucys Bass.Mod but the streaming functions are not yet implmented

They should be.
I've no major issues with streaming and BASS.

You might find using STREAMPROC is a bit of a problem using BlitzMax's standard GC, since it is called from a separate thread. There are ways around that, of course (An example of which is available in the GMEBass module).

Check the latest SVN version of the Bass module. It should have everything you need - written on Mac too, no less :-p


maverick69(Posted 2008) [#3]
Hey Brucey,

thanks for the answer. I have checked out the latest SVN revision of your Bass module, but if I have a look into the bass.bmx file, and the function to stream mp3/shoutcast over Internet is empty :(

Function StreamCreateURL:TBassStream()
End Function


BTW: I'm using it on Mac OS X 10.5.4


Brucey(Posted 2008) [#4]
Ah. *that* streaming...

Okay, I'll have a look at filling in the obvious gaps :-)


maverick69(Posted 2008) [#5]
Yes... *that* streaming ;)

I've also tried to put the Internet-Streaming function into your Bass.mod. The MOD builds fine, but ev'rytime I call the StreamCreateURL Method the app crashes.


Brucey(Posted 2008) [#6]
URL stream support added to the module... currently listening to some Sky.fm :-)
(see example_06 for usage demo).

Currently only tested on Mac, but should work same on Win32.

Lemme know how you get on with that. :-)


maverick69(Posted 2008) [#7]
Wow.... thank you... works great here.

My mistake was, that I gave NULL as parameter as the downloadproc and for some reason this crashed. I saw that you've created a special c function for this reason, where NULL is given by the c-source

HSTREAM bmx_bass_streamcreateurlncb( char *url, DWORD offset, DWORD flags, void *user) {
	return BASS_StreamCreateURL(url, offset, flags, NULL, user);
}


Can you explain why this is required?

Anyway, thank you, very much - you finally made my day. I have tried to get this stream-thing get to work for many hours ;-)


Brucey(Posted 2008) [#8]
Can you explain why this is required?

Not really :-p

It appears to be a quirk of BlitzMax.
In theory, one should be able to pass Null for the callback function, for which NULL/0 would actually be passed. But it appears that some function pointer reference is passed anyway, which BASS attempts to call - resulting in the error message I was getting.
To work around it, I created some glue which would pass a C++ NULL for the callback when none was required.

Seems to work :-)


maverick69(Posted 2008) [#9]
okay, new problem ;)

i wanted to try the callback function to save the stream data:

Function proc(buffer:Byte Ptr, length:Int, user:Object)
	If fstream = Null
		fstream = WriteFile("test.mp3")
	End If
	If buffer = Null
		CloseFile(fstream)
	Else
		For Local i:Int = 0 Until length
			WriteByte(fstream, buffer[i])
		Next
	End If
End Function


In Debug-Mode Blitz always crashes (sometimes a error is written on the debug window, i've never seen before):
kernelErrorToOSStatus: OSStatus = -1 (error = 15)
Debugger Error:scope stack underflow
Debugger Error:scope stack underflow


In Release-Mode it first seems to work, but the ActivityMonitor shows that memory usage of the app grows continuesly (even if I close the stream then and now and reopen it).

Probably, it's just to late now for me. I will have a look at it tomorrow again...


maverick69(Posted 2008) [#10]
Okay, in windows I get an unexceptional memory error evertyime I use a callback function. With I hack it into your glue.cpp everything works fine (weird...)

void CALLBACK downproc(const void * buffer, DWORD length, void * user)
{
    if (!file) file=fopen("test.mp3", "wb"); // create the file
    if (!buffer) fclose(file); // finished downloading
    else fwrite(buffer, 1, length, file);
}

HSTREAM bmx_bass_streamcreateurlncb( char *url, DWORD offset, DWORD flags, void *user) {
	// return BASS_StreamCreateURL(url, offset, flags, NULL, user);
	return BASS_StreamCreateURL(url, offset, flags, &downproc, user);
}



Brucey(Posted 2008) [#11]
I've committed a few fixes which should allow you to pass a BlitzMax callback into the method.

There is a caveat though. Because BASS is running playback in a different thread, you are likely to come across issues using BlitzMax's default GC. This is beacuse it is not designed to be doing two things at the same time - at least, related to the GC.
Using the new GC should get around this issue, as it is thread-safe.

Otherwise... beware ;-)


maverick69(Posted 2008) [#12]
hey Brucey,

I have one more question regarding you're great Bass.MOD:

If I call

GetTags(BASS_TAG_MUSIC_SAMPLE)

after I loaded a XM-Module I get an array of strings of length 1 (the first sample) - but I tought the method should return all samples of the module file. Is this a bug?


Brucey(Posted 2008) [#13]
Hmm. It could be that the strings are multi-byte characters, in which case my use of bbStringFromCString() is a bit flawed (since it looks for ascii 0 as a string terminator... (hence 1 char strings).

Again, I will see what I can do :-)


maverick69(Posted 2008) [#14]
sorry, my fault: your code works :)

I just had a look at the bass documentation, and I have to call

GetTags(BASS_TAG_MUSIC_SAMPLE + no_of_sample)

:-)


maverick69(Posted 2008) [#15]
I have changed your bmx_bass_channelgettags function, so we can recieve ID31 Tags from BlitzMax (changes are done to the glue.cpp)

	typedef struct {
	    char id[3];
	    char title[30];
	    char artist[30];
	    char album[30];
	    char year[4];
	    char comment[30];
	    BYTE genre;
	} TAG_ID3;

[.....]


BBArray * bmx_bass_channelgettags(DWORD handle, DWORD tags) {

	if (tags == BASS_TAG_ID3) {
		TAG_ID3 *id3= (TAG_ID3*) BASS_ChannelGetTags(handle, BASS_TAG_ID3); // get the ID3 tags
		if (id3) {
			
			char buffer[4];
			
			BBArray * p = bbArrayNew1D("$", 7);
			BBString **s = (BBString**)BBARRAYDATA( p,p->dims );
						
			sprintf(buffer, "%.3s", id3->id);
			s[0] = bbStringFromCString(buffer);
			BBRETAIN( s[0] );
			
			s[1] = bbStringFromCString(id3->title);
			BBRETAIN( s[1] );
			
			s[2] = bbStringFromCString(id3->artist);
			BBRETAIN( s[2] );
			
			s[3] = bbStringFromCString(id3->album);
			BBRETAIN( s[3] );

			sprintf(buffer, "%.4s", id3->year);
			s[4] = bbStringFromCString(buffer);
			BBRETAIN( s[4] );
			
			s[5] = bbStringFromCString(id3->comment);
			BBRETAIN( s[5] );
						
			sprintf(buffer, "%d", id3->genre);
			s[6] = bbStringFromCString(buffer);
			BBRETAIN( s[6] );
			
			return p;
		} else {
			return &bbEmptyArray;
		}
	} else {
		const char * text = BASS_ChannelGetTags(handle, tags);
		
		if (text) {
			int count = 0;
			const char * current = text;
			
			while (*current) {
				current += strlen(current) + 1;
				count++;
			}
			
			BBArray * p = bbArrayNew1D("$", count);
			BBString **s = (BBString**)BBARRAYDATA( p,p->dims );
			
			count = 0;
			current = text;
			while (*current) {
				s[count] = bbStringFromCString( current );
				BBRETAIN( s[count] );
	
				current += strlen(current) + 1;
				count++;
			}
			
			return p;
		
		} else {
			return &bbEmptyArray;
		}
	}
}