[xiph-cvs] cvs commit: vorbis-plugins/winamp vorbis.c

Jack Moffitt jack at xiph.org
Tue Feb 20 01:41:58 PST 2001



jack        01/02/20 01:41:58

  Modified:    winamp   vorbis.c
  Log:
  Added Warren Spits' UTF8 patch on behalf of Michael Smith.

Revision  Changes    Path
1.10      +668 -537  vorbis-plugins/winamp/vorbis.c

Index: vorbis.c
===================================================================
RCS file: /usr/local/cvsroot/vorbis-plugins/winamp/vorbis.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- vorbis.c	2000/12/24 06:19:14	1.9
+++ vorbis.c	2001/02/20 09:41:57	1.10
@@ -1,537 +1,668 @@
-// OggVorbis plugin for WinAmp and compatible media player
-// Copyright 2000 Jack Moffitt <jack at icecast.org> 
-//			and Michael Smith <msmith at labyrinth.net.au>
-// HTTP streaming support by Aaron Porter <aaron at javasource.org>
-// Licensed under terms of the LGPL
-
-#include <windows.h>
-#include <mmreg.h>
-#include <msacm.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <process.h>
-
-#include <vorbis/vorbisfile.h>
-//#include <vorbis/os_types.h>
-
-#include "httpstream.h"
-
-#include "in2.h"
-
-
-// post this to the main window at end of file (after playback has stopped)
-#define WM_WA_MPEG_EOF WM_USER + 2
-
-In_Module mod; // the output module (declared near the bottom of this file)
-char lastfn[MAX_PATH]; // currently playing file (used for getting info on the current file)
-int file_length; // file length, in ms
-double decode_pos_ms; // current decoding position, in milliseconds
-int paused; // are we paused?
-int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
-char *sample_buffer; // sample buffer
-
-int samplerate;
-int num_channels;
-int bitrate;
-int current_section = -1;
-OggVorbis_File input_file; // input file handle
-
-int killDecodeThread=0;					// the kill switch for the decode thread
-HANDLE thread_handle=INVALID_HANDLE_VALUE;	// the handle to the decode thread
-
-void DecodeThread(void *b); // the decode thread procedure
-
-void * btdvp=0;
-
-void config(HWND hwndParent)
-{
-	MessageBox(hwndParent,
-		"No configuration. .OGG files are OGG files :):",
-		"Configuration",MB_OK);
-	// if we had a configuration we'd want to write it here :)
-}
-
-void about(HWND hwndParent)
-{
-	MessageBox(hwndParent,"OggVorbis Player, by Jack Moffitt <jack at icecast.org>\n\tand Michael Smith <msmith at labyrinth.net.au>\n\nHTTP streaming by Aaron Porter <aaron at javasource.org>","About OggVorbis Player",MB_OK);
-}
-
-void init() 
-{
-	httpInit();
-}
-
-void quit() 
-{ 
-	httpShutdown();
-}
-
-int isourfile(char *fn) 
-{
-    setHttpVars();
-
-    return isOggUrl(fn);
-} 
-
-size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
-{
-	HANDLE file = (HANDLE)datasource;
-	unsigned long bytesread;
-
-	if(ReadFile(file, ptr, (unsigned long)(size*nmemb), &bytesread, NULL))
-	{
-		return bytesread/size;
-	}
-
-	return 0; /* It failed */
-}
-
-int seek_func(void *datasource, ogg_int64_t offset, int whence)
-{ /* Note that we still need stdio.h even though we don't use stdio, 
-   * in order to get appropriate definitions for SEEK_SET, etc.
-   */
-
-	HANDLE file = (HANDLE)datasource;
-	int seek_type;
-	unsigned long retval;
-	int seek_highword = (int)(offset>>32);
-
-	switch(whence)
-	{
-		case SEEK_SET:
-			seek_type = FILE_BEGIN;
-			break;
-		case SEEK_CUR:
-			seek_type = FILE_CURRENT;
-			break;
-		case SEEK_END:
-			seek_type = FILE_END;
-			break;
-	}
-
-	/* On failure, SetFilePointer returns 0xFFFFFFFF, which is (int)-1 */
-	
-	retval=SetFilePointer(file, (int)(offset&0xffffffff), &seek_highword, seek_type);
-
-	if(retval == 0xFFFFFFFF)
-		return -1;
-	else
-		return 0; /* Exactly mimic stdio return values */
-}
-
-int close_func(void *datasource)
-{
-	HANDLE file = (HANDLE)datasource;
-
-	return (CloseHandle(file)?0:EOF); /* Return value meaning is inverted from fclose() */
-}
-
-long tell_func(void *datasource)
-{
-	HANDLE file = (HANDLE)datasource;
-
-	return (long)SetFilePointer(file, 0, NULL, FILE_CURRENT); /* This returns the right number */
-}
-
-
-int play(char *fn) 
-{ 
-	int maxlatency;
-	HANDLE stream;
-	vorbis_info *vi = NULL;
-	ov_callbacks callbacks = {read_func, seek_func, close_func, tell_func};
-
-    setHttpVars();
-
-	if (isOggUrl(fn))
-	{
-        mod.is_seekable = FALSE;
-
-		if ((btdvp = httpStartBuffering(fn, &input_file, TRUE)) == 0)
-            return -1;
-	}
-    else
-    {
-        mod.is_seekable = TRUE;
-
-	    stream = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
-		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
-	    if (stream == INVALID_HANDLE_VALUE)
-	    {	
-		    return -1;
-	    }
-
-	    if (ov_open_callbacks(stream, &input_file, NULL, 0, callbacks) < 0) {
-		    CloseHandle(stream);
-		    return 1;
-	    }
-    }
-
-	file_length = (int)ov_time_total(&input_file, -1) * 1000;
-	strcpy(lastfn, fn);
-	paused = 0;
-	decode_pos_ms = 0;
-	seek_needed = -1;
-
-	vi = ov_info(&input_file, -1);
-	samplerate = vi->rate;
-	num_channels = vi->channels;
-	bitrate = ov_bitrate(&input_file, -1);
-
-	if(num_channels > 2) /* We can't handle this */
-	{
-		ov_clear(&input_file);
-		return 1;
-	}
-
-	// allocate the sample buffer - it's twice as big as we apparently need,
-	// because mod.dsp_dosamples() may use up to twice as much space. 
-	sample_buffer = malloc(576 * num_channels * 2 * 2 ); 
-	
-	if (sample_buffer == NULL)
-	{
-		ov_clear(&input_file);
-		return 1;
-	}
-
-	maxlatency = mod.outMod->Open(samplerate, num_channels, 16, -1, -1);
-	if (maxlatency < 0) {
-		// error opening device
-		ov_clear(&input_file);
-		return 1;
-	}
-	
-	// dividing by 1000 for the first parameter of setinfo makes it
-	// display 'H'... for hundred.. i.e. 14H Kbps.
-	mod.SetInfo(bitrate / 1000, samplerate / 1000, num_channels, 1);
-
-	// initialize vis stuff
-	mod.SAVSAInit(maxlatency, samplerate);
-	mod.VSASetInfo(samplerate, num_channels);
-
-	mod.outMod->SetVolume(-666); // set the output plug-ins default volume
-
-	killDecodeThread = 0;
-	thread_handle = (HANDLE)_beginthread(DecodeThread,0, (void *)(&killDecodeThread) );
-	
-	return 0; 
-}
-
-void pause() 
-{
-	paused = 1; 
-	mod.outMod->Pause(1); 
-}
-
-void unpause() 
-{ 
-	paused = 0; 
-	mod.outMod->Pause(0); 
-}
-
-int ispaused() 
-{ 
-	return paused; 
-}
-
-void stop() 
-{ 
-	if (btdvp)
-    {
-		httpStopBuffering(btdvp);
-        btdvp = 0;
-    }
-
-	if (thread_handle != INVALID_HANDLE_VALUE) {
-		killDecodeThread = 1;
-		
-		if (WaitForSingleObject(thread_handle, INFINITE) == WAIT_TIMEOUT) {
-			MessageBox(mod.hMainWindow, "error asking thread to die!\n", "error killing decode thread", 0);
-			TerminateThread(thread_handle, 0);
-		}
-		ov_clear(&input_file);
-		thread_handle = INVALID_HANDLE_VALUE;
-
-	}
-
-	// deallocate sample buffer
-	if (sample_buffer)
-		free(sample_buffer);
-
-	mod.outMod->Close();
-
-	mod.SAVSADeInit();
-}
-
-int getlength() { 
-	return file_length; 
-}
-
-int getoutputtime() { 
-	return ((long)decode_pos_ms) + (mod.outMod->GetOutputTime() - mod.outMod->GetWrittenTime()); 
-}
-
-void setoutputtime(int time_in_ms) { 
-	seek_needed = time_in_ms; 
-}
-
-void setvolume(int volume) 
-{ 
-	mod.outMod->SetVolume(volume); 
-}
-
-void setpan(int pan) 
-{ 
-	mod.outMod->SetPan(pan); 
-}
-
-int infoDlg(char *fn, HWND hwnd)
-{
-	MessageBox(mod.hMainWindow, "Sorry, there is currently no interface to set or read information.", "Unimplemented", MB_OK);
-	// TODO: implement info dialog. 
-	return 0;
-}
-
-char *generate_title(vorbis_comment *comment, char *fn)
-{/* Later, extend this to be configurable like the mp3 player */
-	char buff[1024];
-	char *title, *artist, *finaltitle;
-
-	title = vorbis_comment_query(comment, "title", 0);
-	artist = vorbis_comment_query(comment, "artist", 0);
-
-	if(artist && title)
-		_snprintf(buff, 1024, "%s - %s", artist, title);
-	else if(title)
-		_snprintf(buff, 1024, "%s", title);
-	else if(artist)
-		_snprintf(buff, 1024, "%s - unknown", artist);
-	else
-    {
-	    if (title = httpGetTitle(fn))
-		    return title;
-
-		_snprintf(buff, 1024, "%s (no title)", fn);
-    }
-
-	finaltitle = strdup(buff);
-
-	return finaltitle;
-}
-
-void getfileinfo(char *filename, char *title, int *length_in_ms)
-{
-	HANDLE stream;
-	OggVorbis_File vf;
-	vorbis_comment *comment;
-	ov_callbacks callbacks = {read_func, seek_func, close_func, tell_func};
-
-
-	if (filename != NULL && filename[0] != 0)
-    {
-        if (isOggUrl(filename))
-        {
-            if (!httpStartBuffering(filename, &vf, FALSE))
-                return;
-        }
-        else
-        {
-		    stream = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
-			    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
-		    if(stream == INVALID_HANDLE_VALUE)
-			    return;
-
-		    // The ov_open() function performs full stream detection and machine
-		    // initialization.  If it returns 0, the stream *is* Vorbis and we're
-		    // fully ready to decode.
-		    
-
-		    if (ov_open_callbacks(stream, &vf, NULL, 0, callbacks) < 0) {
-			    CloseHandle(stream);
-			    return;
-		    }
-        }
-
-		file_length = (int)ov_time_total(&vf, -1) * 1000;
-		*length_in_ms = file_length;
-
-		comment = ov_comment(&vf, -1);
-		if(comment)
-		{
-			char *gen_title = generate_title(comment, filename);
-			if(gen_title)
-			{
-				strcpy(title, gen_title);
-				free(gen_title);
-			}
-		}
-		else
-			strcpy(title,filename);
-
-		// once the ov_open() succeeds, the file belongs to vorbisfile.
-		// ov_clear() will close it.
-	
-		ov_clear(&vf);
-		
-	} else {
-		/* This is the only section of code which uses vorbisfile calls 
-		   in one thread whilst the main playback thread is running. 
-		   Technically, we should protect it with critical sections, but
-		   these two calls appear to be safe on win32/x86 */
-
-		comment = ov_comment(&input_file, -1);
-		if(comment)
-		{
-			char *gen_title = generate_title(comment, lastfn);
-			if(gen_title)
-			{
-				strcpy(title, gen_title);
-				free(gen_title);
-			}
-		}
-   		else
-			strcpy(title, filename);
-
-		*length_in_ms = (int)ov_time_total(&input_file, -1) * 1000;
-
-	}
-}
-
-void eq_set(int on, char data[10], int preamp) 
-{ 
-	/* Waiting on appropriate libvorbis API additions. */
-}
-
-
-// render 576 samples into buf. 
-// note that if you adjust the size of sample_buffer, for say, 1024
-// sample blocks, it will still work, but some of the visualization 
-// might not look as good as it could. Stick with 576 sample blocks
-// if you can, and have an additional auxiliary (overflow) buffer if 
-// necessary.. 
-int get_576_samples(char *buf)
-{
-	int ret;
-
-	ret = ov_read(&input_file, buf, 576 * num_channels * 2, 0, 2, 1, &current_section);
-
-	return ret;
-}
-
-void DecodeThread(void *b)
-{
-	int eos = 0;
-	int lostsync = 0;
-	double lastupdate = 0;
-
-	while (!*((int *)b)) {
-		if (seek_needed != -1) {
-			decode_pos_ms = (double)seek_needed;// - (seek_needed % 1000);
-			lastupdate = decode_pos_ms;
-			seek_needed = -1;
-			eos = 0;
-			mod.outMod->Flush((long)decode_pos_ms);
-
-			ov_time_seek(&input_file, decode_pos_ms / 1000);
-		}
-
-		if (eos) {
-			mod.outMod->CanWrite();
-			
-			if (!mod.outMod->IsPlaying()) {
-				PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
-				return;
-			}
-			
-			Sleep(10);
-		} else if (mod.outMod->CanWrite() >= ((576 * num_channels * 2) << (mod.dsp_isactive() ? 1 : 0))) {	
-			int ret;
-			ret = get_576_samples(sample_buffer);
-			
-			if (ret == 0) {
-				// eof
-				eos = 1;
-			}
-			else if(ret < 0)
-			{
-				/* Hole in data, lost sync, or something like that */
-				/* Inform winamp that we lost sync */
-				mod.SetInfo(bitrate / 1000, samplerate / 1000, num_channels, 0);
-				lostsync = 1;
-			}
-			else {
-				/* Update current bitrate (not currently implemented), and set sync (if lost) */
-				if(lostsync || (decode_pos_ms - lastupdate > 500))
-				{
-					bitrate = ov_bitrate_instant(&input_file);
-					mod.SetInfo(bitrate / 1000, samplerate / 1000, num_channels, 1);
-					lostsync = 0;
-					lastupdate = decode_pos_ms;
-				}
-				mod.SAAddPCMData((char *)sample_buffer, num_channels, 16, (long)decode_pos_ms);
-				mod.VSAAddPCMData((char *)sample_buffer, num_channels, 16, (long)decode_pos_ms);
-				decode_pos_ms += (ret/(2*num_channels) * 1000) / (float)samplerate;
-								
-				if (mod.dsp_isactive()) 
-					ret = mod.dsp_dosamples((short *)sample_buffer, ret / num_channels / (2), 16, num_channels, samplerate) * (num_channels * (2));
-				
-				mod.outMod->Write(sample_buffer, ret);
-			}
-		} else {
-			Sleep(20);
-		}
-	}
-	
-	_endthread();
-}
-
-In_Module mod = 
-{
-	IN_VER,
-	"OggVorbis Input Plugin 0.2",
-	0,	// hMainWindow
-	0,  // hDllInstance
-	"OGG\0OggVorbis File (*.OGG)\0",
-	1,	// is_seekable
-	1, // uses output
-	config,
-	about,
-	init,
-	quit,
-	getfileinfo,
-	infoDlg,
-	isourfile,
-	play,
-	pause,
-	unpause,
-	ispaused,
-	stop,
-	
-	getlength,
-	getoutputtime,
-	setoutputtime,
-
-	setvolume,
-	setpan,
-
-	0,0,0,0,0,0,0,0,0, // vis stuff
-
-
-	0,0, // dsp
- 
-	eq_set,
-
-	NULL,		// setinfo
-
-	0 // out_mod
-
-};
-
-__declspec( dllexport ) In_Module * winampGetInModule2()
-{
-	return &mod;
-}
+// OggVorbis plugin for WinAmp and compatible media player
+// Copyright 2000 Jack Moffitt <jack at icecast.org> 
+//			and Michael Smith <msmith at labyrinth.net.au>
+// HTTP streaming support by Aaron Porter <aaron at javasource.org>
+// Licensed under terms of the LGPL
+
+#include <windows.h>
+#include <mmreg.h>
+#include <msacm.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <process.h>
+
+#include <vorbis/vorbisfile.h>
+//#include <vorbis/os_types.h>
+
+#include "httpstream.h"
+
+#include "in2.h"
+
+
+// post this to the main window at end of file (after playback has stopped)
+#define WM_WA_MPEG_EOF WM_USER + 2
+
+#define UTF8_ILSEQ	-1
+
+In_Module mod; // the output module (declared near the bottom of this file)
+char lastfn[MAX_PATH]; // currently playing file (used for getting info on the current file)
+int file_length; // file length, in ms
+double decode_pos_ms; // current decoding position, in milliseconds
+int paused; // are we paused?
+int seek_needed; // if != -1, it is the point that the decode thread should seek to, in ms.
+char *sample_buffer; // sample buffer
+
+int samplerate;
+int num_channels;
+int bitrate;
+int current_section = -1;
+OggVorbis_File input_file; // input file handle
+
+int killDecodeThread=0;					// the kill switch for the decode thread
+HANDLE thread_handle=INVALID_HANDLE_VALUE;	// the handle to the decode thread
+
+void DecodeThread(void *b); // the decode thread procedure
+
+void * btdvp=0;
+
+void config(HWND hwndParent)
+{
+	MessageBox(hwndParent,
+		"No configuration. .OGG files are OGG files :):",
+		"Configuration",MB_OK);
+	// if we had a configuration we'd want to write it here :)
+}
+
+void about(HWND hwndParent)
+{
+	MessageBox(hwndParent,"OggVorbis Player, by Jack Moffitt <jack at icecast.org>\n\tand Michael Smith <msmith at labyrinth.net.au>\n\nHTTP streaming by Aaron Porter <aaron at javasource.org>","About OggVorbis Player",MB_OK);
+}
+
+void init() 
+{
+	httpInit();
+}
+
+void quit() 
+{ 
+	httpShutdown();
+}
+
+int isourfile(char *fn) 
+{
+    setHttpVars();
+
+    return isOggUrl(fn);
+} 
+
+/* Converts a UTF-8 character sequence to a UCS-4 character */
+int _utf8_to_ucs4(unsigned int *target, const char *utf8, int n)
+{
+	unsigned int result = 0;
+	int count;
+	int i;
+
+	/* Determine the number of characters in sequence */
+	if ((*utf8 & 0x80) == 0)
+		count = 1;
+	else if ((*utf8 & 0xE0) == 0xC0)
+		count = 2;
+	else if ((*utf8 & 0xF0) == 0xE0)
+		count = 3;
+	else if ((*utf8 & 0xF8) == 0xF0)
+		count = 4;
+	else if ((*utf8 & 0xFC) == 0xF8)
+		count = 5;
+	else if ((*utf8 & 0xFE) == 0xFC)
+		count = 6;
+	else
+		return UTF8_ILSEQ; /* Invalid start byte */
+
+	if (n < count)
+		return UTF8_ILSEQ; /* Not enough characters */
+
+	if (count == 2 && (*utf8 & 0x1E) == 0)
+		return UTF8_ILSEQ; /* Overlong sequence */
+
+	/* Convert the first character */
+	if (count == 1)
+		result = *utf8;
+	else
+		result = (0xFF >> (count +1)) & *utf8;
+
+	/* Convert the continuation bytes */
+	for (i = 1; i < count; i++)
+	{
+		if ((utf8[i] & 0xC0) != 0x80)
+			return UTF8_ILSEQ; /* Not a continuation byte */
+		if (result == 0 &&
+			i == 2 &&
+			((utf8[i] & 0x7F) >> (7 - count)) == 0)
+			return UTF8_ILSEQ; /* Overlong sequence */
+		result = (result << 6) | (utf8[i] & 0x3F);
+	}
+
+	if (target != 0)
+		*target = result;
+
+	return count;
+}
+
+/* Converts a UTF-8 string to a WCHAR string */
+int UTF8ToWideChar(LPWSTR target, LPCSTR utf8, WCHAR unknown)
+{
+	int wcount = 0;
+	int conv;
+	unsigned int ucs4;
+	int count = lstrlenA(utf8) +1;
+
+	while (count != 0)
+	{
+		conv = _utf8_to_ucs4(&ucs4, utf8, count);
+		if (conv == UTF8_ILSEQ) return UTF8_ILSEQ;
+		if (target != 0)
+		{
+			if (ucs4 > 0xFFFF)
+				*target = unknown; /* Can only handle BMP */
+			else
+				*target = (WCHAR) ucs4;
+			target++;
+		}
+		wcount++;
+		count -= conv;
+		utf8 += conv;
+	}
+
+	return wcount;
+}
+
+size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+	HANDLE file = (HANDLE)datasource;
+	unsigned long bytesread;
+
+	if(ReadFile(file, ptr, (unsigned long)(size*nmemb), &bytesread, NULL))
+	{
+		return bytesread/size;
+	}
+
+	return 0; /* It failed */
+}
+
+int seek_func(void *datasource, ogg_int64_t offset, int whence)
+{ /* Note that we still need stdio.h even though we don't use stdio, 
+   * in order to get appropriate definitions for SEEK_SET, etc.
+   */
+
+	HANDLE file = (HANDLE)datasource;
+	int seek_type;
+	unsigned long retval;
+	int seek_highword = (int)(offset>>32);
+
+	switch(whence)
+	{
+		case SEEK_SET:
+			seek_type = FILE_BEGIN;
+			break;
+		case SEEK_CUR:
+			seek_type = FILE_CURRENT;
+			break;
+		case SEEK_END:
+			seek_type = FILE_END;
+			break;
+	}
+
+	/* On failure, SetFilePointer returns 0xFFFFFFFF, which is (int)-1 */
+	
+	retval=SetFilePointer(file, (int)(offset&0xffffffff), &seek_highword, seek_type);
+
+	if(retval == 0xFFFFFFFF)
+		return -1;
+	else
+		return 0; /* Exactly mimic stdio return values */
+}
+
+int close_func(void *datasource)
+{
+	HANDLE file = (HANDLE)datasource;
+
+	return (CloseHandle(file)?0:EOF); /* Return value meaning is inverted from fclose() */
+}
+
+long tell_func(void *datasource)
+{
+	HANDLE file = (HANDLE)datasource;
+
+	return (long)SetFilePointer(file, 0, NULL, FILE_CURRENT); /* This returns the right number */
+}
+
+
+int play(char *fn) 
+{ 
+	int maxlatency;
+	HANDLE stream;
+	vorbis_info *vi = NULL;
+	ov_callbacks callbacks = {read_func, seek_func, close_func, tell_func};
+
+    setHttpVars();
+
+	if (isOggUrl(fn))
+	{
+        mod.is_seekable = FALSE;
+
+		if ((btdvp = httpStartBuffering(fn, &input_file, TRUE)) == 0)
+            return -1;
+	}
+    else
+    {
+        mod.is_seekable = TRUE;
+
+	    stream = CreateFile(fn, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+	    if (stream == INVALID_HANDLE_VALUE)
+	    {	
+		    return -1;
+	    }
+
+	    if (ov_open_callbacks(stream, &input_file, NULL, 0, callbacks) < 0) {
+		    CloseHandle(stream);
+		    return 1;
+	    }
+    }
+
+	file_length = (int)ov_time_total(&input_file, -1) * 1000;
+	strcpy(lastfn, fn);
+	paused = 0;
+	decode_pos_ms = 0;
+	seek_needed = -1;
+
+	vi = ov_info(&input_file, -1);
+	samplerate = vi->rate;
+	num_channels = vi->channels;
+	bitrate = ov_bitrate(&input_file, -1);
+
+	if(num_channels > 2) /* We can't handle this */
+	{
+		ov_clear(&input_file);
+		return 1;
+	}
+
+	// allocate the sample buffer - it's twice as big as we apparently need,
+	// because mod.dsp_dosamples() may use up to twice as much space. 
+	sample_buffer = malloc(576 * num_channels * 2 * 2 ); 
+	
+	if (sample_buffer == NULL)
+	{
+		ov_clear(&input_file);
+		return 1;
+	}
+
+	maxlatency = mod.outMod->Open(samplerate, num_channels, 16, -1, -1);
+	if (maxlatency < 0) {
+		// error opening device
+		ov_clear(&input_file);
+		return 1;
+	}
+	
+	// dividing by 1000 for the first parameter of setinfo makes it
+	// display 'H'... for hundred.. i.e. 14H Kbps.
+	mod.SetInfo(bitrate / 1000, samplerate / 1000, num_channels, 1);
+
+	// initialize vis stuff
+	mod.SAVSAInit(maxlatency, samplerate);
+	mod.VSASetInfo(samplerate, num_channels);
+
+	mod.outMod->SetVolume(-666); // set the output plug-ins default volume
+
+	killDecodeThread = 0;
+	thread_handle = (HANDLE)_beginthread(DecodeThread,0, (void *)(&killDecodeThread) );
+	
+	return 0; 
+}
+
+void pause() 
+{
+	paused = 1; 
+	mod.outMod->Pause(1); 
+}
+
+void unpause() 
+{ 
+	paused = 0; 
+	mod.outMod->Pause(0); 
+}
+
+int ispaused() 
+{ 
+	return paused; 
+}
+
+void stop() 
+{ 
+	if (btdvp)
+    {
+		httpStopBuffering(btdvp);
+        btdvp = 0;
+    }
+
+	if (thread_handle != INVALID_HANDLE_VALUE) {
+		killDecodeThread = 1;
+		
+		if (WaitForSingleObject(thread_handle, INFINITE) == WAIT_TIMEOUT) {
+			MessageBox(mod.hMainWindow, "error asking thread to die!\n", "error killing decode thread", 0);
+			TerminateThread(thread_handle, 0);
+		}
+		ov_clear(&input_file);
+		thread_handle = INVALID_HANDLE_VALUE;
+
+	}
+
+	// deallocate sample buffer
+	if (sample_buffer)
+		free(sample_buffer);
+
+	mod.outMod->Close();
+
+	mod.SAVSADeInit();
+}
+
+int getlength() { 
+	return file_length; 
+}
+
+int getoutputtime() { 
+	return ((long)decode_pos_ms) + (mod.outMod->GetOutputTime() - mod.outMod->GetWrittenTime()); 
+}
+
+void setoutputtime(int time_in_ms) { 
+	seek_needed = time_in_ms; 
+}
+
+void setvolume(int volume) 
+{ 
+	mod.outMod->SetVolume(volume); 
+}
+
+void setpan(int pan) 
+{ 
+	mod.outMod->SetPan(pan); 
+}
+
+int infoDlg(char *fn, HWND hwnd)
+{
+	MessageBox(mod.hMainWindow, "Sorry, there is currently no interface to set or read information.", "Unimplemented", MB_OK);
+	// TODO: implement info dialog. 
+	return 0;
+}
+
+char *generate_title(vorbis_comment *comment, char *fn)
+{/* Later, extend this to be configurable like the mp3 player */
+	int len;
+	char buff[1024];
+	char *title, *artist, *finaltitle;
+	LPWSTR titleW, artistW;
+	LPSTR titleL = NULL, artistL = NULL;
+
+	title = vorbis_comment_query(comment, "title", 0);
+	artist = vorbis_comment_query(comment, "artist", 0);
+
+	if (title)
+	{
+		/* Convert the UTF-8 title to the system code page */
+		len = UTF8ToWideChar(NULL, title, '?');
+		if (len == UTF8_ILSEQ)
+		{
+			/* Fallback to ascii */
+			titleL = strdup(title);
+		}
+		else
+		{
+			/* Convert the UTF-8 string */
+			titleL = calloc(len, sizeof(CHAR));
+			titleW = calloc(len, sizeof(WCHAR));
+			UTF8ToWideChar(titleW, title, '?');
+			WideCharToMultiByte(CP_ACP, 0, titleW, -1, titleL,
+			                    len, "?", NULL);
+			free(titleW);
+		}
+	}
+
+	if (artist)
+	{
+		/* Convert the UTF-8 artist to the system code page */
+		len = UTF8ToWideChar(NULL, artist, '?');
+		if (len == UTF8_ILSEQ)
+		{
+			/* Fallback to ascii */
+			artistL = strdup(artist);
+		}
+		else
+		{
+			/* Convert the UTF-8 string */
+			artistL = calloc(len, sizeof(CHAR));
+			artistW = calloc(len, sizeof(WCHAR));
+			UTF8ToWideChar(artistW, artist, '?');
+			WideCharToMultiByte(CP_ACP, 0, artistW, -1, artistL,
+			                    len, "?", NULL);
+			free(artistW);
+		}
+	}
+
+
+	if(artist && title)
+		_snprintf(buff, 1024, "%s - %s", artistL, titleL);
+	else if(title)
+		_snprintf(buff, 1024, "%s", titleL);
+	else if(artist)
+		_snprintf(buff, 1024, "%s - unknown", artistL);
+	else
+    {
+	    if (title = httpGetTitle(fn))
+		    return title;
+
+		_snprintf(buff, 1024, "%s (no title)", fn);
+    }
+
+	free(artistL);
+	free(titleL);
+	finaltitle = strdup(buff);
+
+	return finaltitle;
+}
+
+void getfileinfo(char *filename, char *title, int *length_in_ms)
+{
+	HANDLE stream;
+	OggVorbis_File vf;
+	vorbis_comment *comment;
+	ov_callbacks callbacks = {read_func, seek_func, close_func, tell_func};
+
+
+	if (filename != NULL && filename[0] != 0)
+    {
+        if (isOggUrl(filename))
+        {
+            if (!httpStartBuffering(filename, &vf, FALSE))
+                return;
+        }
+        else
+        {
+		    stream = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+			    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+		    if(stream == INVALID_HANDLE_VALUE)
+			    return;
+
+		    // The ov_open() function performs full stream detection and machine
+		    // initialization.  If it returns 0, the stream *is* Vorbis and we're
+		    // fully ready to decode.
+		    
+
+		    if (ov_open_callbacks(stream, &vf, NULL, 0, callbacks) < 0) {
+			    CloseHandle(stream);
+			    return;
+		    }
+        }
+
+		file_length = (int)ov_time_total(&vf, -1) * 1000;
+		*length_in_ms = file_length;
+
+		comment = ov_comment(&vf, -1);
+		if(comment)
+		{
+			char *gen_title = generate_title(comment, filename);
+			if(gen_title)
+			{
+				strcpy(title, gen_title);
+				free(gen_title);
+			}
+		}
+		else
+			strcpy(title,filename);
+
+		// once the ov_open() succeeds, the file belongs to vorbisfile.
+		// ov_clear() will close it.
+	
+		ov_clear(&vf);
+		
+	} else {
+		/* This is the only section of code which uses vorbisfile calls 
+		   in one thread whilst the main playback thread is running. 
+		   Technically, we should protect it with critical sections, but
+		   these two calls appear to be safe on win32/x86 */
+
+		comment = ov_comment(&input_file, -1);
+		if(comment)
+		{
+			char *gen_title = generate_title(comment, lastfn);
+			if(gen_title)
+			{
+				strcpy(title, gen_title);
+				free(gen_title);
+			}
+		}
+   		else
+			strcpy(title, filename);
+
+		*length_in_ms = (int)ov_time_total(&input_file, -1) * 1000;
+
+	}
+}
+
+void eq_set(int on, char data[10], int preamp) 
+{ 
+	/* Waiting on appropriate libvorbis API additions. */
+}
+
+
+// render 576 samples into buf. 
+// note that if you adjust the size of sample_buffer, for say, 1024
+// sample blocks, it will still work, but some of the visualization 
+// might not look as good as it could. Stick with 576 sample blocks
+// if you can, and have an additional auxiliary (overflow) buffer if 
+// necessary.. 
+int get_576_samples(char *buf)
+{
+	int ret;
+
+	ret = ov_read(&input_file, buf, 576 * num_channels * 2, 0, 2, 1, &current_section);
+
+	return ret;
+}
+
+void DecodeThread(void *b)
+{
+	int eos = 0;
+	int lostsync = 0;
+	double lastupdate = 0;
+
+	while (!*((int *)b)) {
+		if (seek_needed != -1) {
+			decode_pos_ms = (double)seek_needed;// - (seek_needed % 1000);
+			lastupdate = decode_pos_ms;
+			seek_needed = -1;
+			eos = 0;
+			mod.outMod->Flush((long)decode_pos_ms);
+
+			ov_time_seek(&input_file, decode_pos_ms / 1000);
+		}
+
+		if (eos) {
+			mod.outMod->CanWrite();
+			
+			if (!mod.outMod->IsPlaying()) {
+				PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
+				return;
+			}
+			
+			Sleep(10);
+		} else if (mod.outMod->CanWrite() >= ((576 * num_channels * 2) << (mod.dsp_isactive() ? 1 : 0))) {	
+			int ret;
+			ret = get_576_samples(sample_buffer);
+			
+			if (ret == 0) {
+				// eof
+				eos = 1;
+			}
+			else if(ret < 0)
+			{
+				/* Hole in data, lost sync, or something like that */
+				/* Inform winamp that we lost sync */
+				mod.SetInfo(bitrate / 1000, samplerate / 1000, num_channels, 0);
+				lostsync = 1;
+			}
+			else {
+				/* Update current bitrate (not currently implemented), and set sync (if lost) */
+				if(lostsync || (decode_pos_ms - lastupdate > 500))
+				{
+					bitrate = ov_bitrate_instant(&input_file);
+					mod.SetInfo(bitrate / 1000, samplerate / 1000, num_channels, 1);
+					lostsync = 0;
+					lastupdate = decode_pos_ms;
+				}
+				mod.SAAddPCMData((char *)sample_buffer, num_channels, 16, (long)decode_pos_ms);
+				mod.VSAAddPCMData((char *)sample_buffer, num_channels, 16, (long)decode_pos_ms);
+				decode_pos_ms += (ret/(2*num_channels) * 1000) / (float)samplerate;
+								
+				if (mod.dsp_isactive()) 
+					ret = mod.dsp_dosamples((short *)sample_buffer, ret / num_channels / (2), 16, num_channels, samplerate) * (num_channels * (2));
+				
+				mod.outMod->Write(sample_buffer, ret);
+			}
+		} else {
+			Sleep(20);
+		}
+	}
+	
+	_endthread();
+}
+
+In_Module mod = 
+{
+	IN_VER,
+	"OggVorbis Input Plugin 0.2",
+	0,	// hMainWindow
+	0,  // hDllInstance
+	"OGG\0OggVorbis File (*.OGG)\0",
+	1,	// is_seekable
+	1, // uses output
+	config,
+	about,
+	init,
+	quit,
+	getfileinfo,
+	infoDlg,
+	isourfile,
+	play,
+	pause,
+	unpause,
+	ispaused,
+	stop,
+	
+	getlength,
+	getoutputtime,
+	setoutputtime,
+
+	setvolume,
+	setpan,
+
+	0,0,0,0,0,0,0,0,0, // vis stuff
+
+
+	0,0, // dsp
+ 
+	eq_set,
+
+	NULL,		// setinfo
+
+	0 // out_mod
+
+};
+
+__declspec( dllexport ) In_Module * winampGetInModule2()
+{
+	return &mod;
+}

--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list