mirror of
https://github.com/projectM-visualizer/projectm.git
synced 2026-02-08 07:15:24 +00:00
git-svn-id: https://projectm.svn.sourceforge.net/svnroot/projectm/personal/carm/dev-1.0@256 6778bc44-b910-0410-a7a0-be141de4315d
892 lines
29 KiB
C++
Executable File
892 lines
29 KiB
C++
Executable File
/**
|
|
* projectM -- Milkdrop-esque visualisation SDK
|
|
* Copyright (C)2003-2007 projectM Team
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
* See 'LICENSE.txt' included within this release
|
|
*
|
|
*/
|
|
/**
|
|
* $Id: iprojectM.c,v 1.10 2004/11/07 17:29:04 cvs Exp $
|
|
*
|
|
* iTunes Plugin Wrapper for projectM
|
|
*/
|
|
|
|
#include <string.h>
|
|
#ifdef WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include <projectM-engine/carbontoprojectM.hpp>
|
|
#include <projectM-engine/Common.hpp>
|
|
#include <projectM-engine/BeatDetect.hpp>
|
|
#include <projectM-engine/PCM.hpp>
|
|
#include <projectM-engine/projectM.hpp>
|
|
//#include <projectM-engine/menu.h>
|
|
|
|
#ifdef WIN32
|
|
|
|
#include "win32/iTunesVisualAPI.h"
|
|
#else
|
|
#ifdef MACOS
|
|
#include "macos/iTunesVisualAPI.h"
|
|
#include <agl.h>
|
|
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
|
|
#ifdef WIN32
|
|
// window information
|
|
HWND windowHandle;
|
|
HDC windowDC;
|
|
HGLRC windowRC;
|
|
#endif /** WIN32 */
|
|
|
|
#ifdef MACOS
|
|
CGrafPtr port = NULL;
|
|
AGLContext context = NULL;
|
|
int frameCount = 0;
|
|
CFDictionaryRef desktopMode = NULL;
|
|
CFDictionaryRef fullscreenMode = NULL;
|
|
int inFullScreenMode = 0;
|
|
int displayCaptured = 0;
|
|
boolean_t exactMatch;
|
|
int fullscreenWidth, fullscreenHeight;
|
|
#endif /** MACOS */
|
|
|
|
#ifdef MACOS
|
|
#define kTVisualPluginName "\pprojectM"
|
|
#define kTVisualPluginCreator 'hook'
|
|
#define kTVisualPluginMajorVersion 1
|
|
#define kTVisualPluginMinorVersion 0
|
|
#define kTVisualPluginReleaseStage finalStage
|
|
#define kTVisualPluginNonFinalRelease 0
|
|
#endif /** MACOS */
|
|
|
|
/** Port dimensions */
|
|
int windowWidth;
|
|
int windowHeight;
|
|
|
|
/** projectM info */
|
|
projectM *globalPM = NULL;
|
|
#ifdef DEBUG
|
|
FILE *debugFile = NULL;
|
|
#endif
|
|
|
|
// iTunes information
|
|
RenderVisualData renderData;
|
|
int playing;
|
|
void *appCookie;
|
|
ITAppProcPtr appProc;
|
|
#ifdef WIN32
|
|
ITTrackInfoV1 trackInfo;
|
|
ITStreamInfoV1 streamInfo;
|
|
#else
|
|
#ifdef MACOS
|
|
ITTrackInfo trackInfo;
|
|
ITStreamInfo streamInfo;
|
|
FSSpec pluginSpec;
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
|
|
#ifdef MACOS
|
|
void CleanupGL();
|
|
#endif
|
|
|
|
#ifdef MACOS
|
|
/** fss2path takes the FSSpec of the plugin and returns the filename */
|
|
void fss2path( FSSpec *fss, char *path ) {
|
|
|
|
int l;
|
|
|
|
for ( l = 0 ; l < (fss->name[0]) ; l++ ) {
|
|
path[l] = fss->name[l + 1];
|
|
}
|
|
path[l] = '\0';
|
|
|
|
DWRITE( "fss2path: '%s'\n", path );
|
|
|
|
if ( fss->parID != fsRtParID) {
|
|
int i, len;
|
|
CInfoPBRec pb;
|
|
|
|
pb.dirInfo.ioNamePtr = fss->name;
|
|
pb.dirInfo.ioVRefNum = fss->vRefNum;
|
|
pb.dirInfo.ioDrParID = fss->parID;
|
|
|
|
do {
|
|
pb.dirInfo.ioFDirIndex = -1;
|
|
pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
|
|
|
|
if ( PBGetCatInfoSync( &pb ) != noErr ) {
|
|
break;
|
|
}
|
|
|
|
len = fss->name[0] + 1;
|
|
for ( i = l ; i >= 0 ; i-- ) path[i + len] = path[i];
|
|
for ( i = 1 ; i < len ; i++ ) path[i - 1] = fss->name[i];
|
|
|
|
path[i - 1] = '/';
|
|
l += len;
|
|
} while ( pb.dirInfo.ioDrDirID != fsRtDirID );
|
|
|
|
len = 9;
|
|
for ( i = l ; i >= 0 ; i-- ) path[i + len] = path[i];
|
|
path[0] = '/';
|
|
path[1] = 'V';
|
|
path[2] = 'o';
|
|
path[3] = 'l';
|
|
path[4] = 'u';
|
|
path[5] = 'm';
|
|
path[6] = 'e';
|
|
path[7] = 's';
|
|
path[8] = '/';
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// create OpenGL rendering context for the specified window
|
|
#ifdef WIN32
|
|
void InitializeGL(HWND hwnd)
|
|
{
|
|
int pixelFormat;
|
|
PIXELFORMATDESCRIPTOR pfd = {
|
|
sizeof (PIXELFORMATDESCRIPTOR), /* struct size */
|
|
1, /* Version number */
|
|
PFD_DRAW_TO_WINDOW /* Flags, draw to a window, */
|
|
| PFD_DOUBLEBUFFER /* Requires Doublebuffer hw */
|
|
| PFD_SUPPORT_OPENGL, /* use OpenGL */
|
|
PFD_TYPE_RGBA, /* RGBA pixel values */
|
|
32, /* 24-bit color */
|
|
0, 0, 0, /* RGB bits & shift sizes. */
|
|
0, 0, 0, /* Don't care about them */
|
|
0, 0, /* No alpha buffer info */
|
|
0, 0, 0, 0, 0, /* No accumulation buffer */
|
|
16, /* depth buffer */
|
|
1, /* stencil buffer */
|
|
0, /* No auxiliary buffers */
|
|
PFD_MAIN_PLANE, /* Layer type */
|
|
0, /* Reserved (must be 0) */
|
|
0, /* No layer mask */
|
|
0, /* No visible mask */
|
|
0 /* No damage mask */
|
|
};
|
|
|
|
windowHandle = hwnd;
|
|
windowDC = GetDC(windowHandle);
|
|
|
|
// memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
|
|
|
|
pixelFormat = ChoosePixelFormat(windowDC, &pfd);
|
|
if (pixelFormat == 0)
|
|
return;
|
|
|
|
if (SetPixelFormat(windowDC, pixelFormat, &pfd) == 0)
|
|
return;
|
|
|
|
windowRC = wglCreateContext(windowDC);
|
|
if (windowRC == NULL)
|
|
return;
|
|
|
|
if (!wglMakeCurrent(windowDC, windowRC))
|
|
return;
|
|
|
|
#ifdef PANTS
|
|
// enable v-sync if WGL_EXT_swap_control is supported
|
|
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
|
|
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
|
|
if (wglSwapIntervalEXT != NULL)
|
|
wglSwapIntervalEXT(1);
|
|
#endif
|
|
|
|
/** Initialise projectM */
|
|
#ifdef DEBUG
|
|
debugFile = fopen( "c:\\iprojectM.txt", "wb" );
|
|
#endif
|
|
globalPM = (projectM_t *)malloc( sizeof( projectM_t ) );
|
|
projectM_reset( globalPM );
|
|
|
|
globalPM->fullscreen = 0;
|
|
globalPM->usePbuffers = 1;
|
|
globalPM->renderTarget->texsize = 1024;
|
|
globalPM->showfps = 1;
|
|
|
|
/** Initialise font and preset paths */
|
|
globalPM->fontURL = (char *)malloc( sizeof( char ) * 1024 );
|
|
strcpy( globalPM->fontURL, "c:\\Program Files\\iTunes\\Plug-ins\\projectM\\fonts" );
|
|
|
|
globalPM->presetURL = (char *)malloc( sizeof( char ) * 1024 );
|
|
strcpy( globalPM->presetURL, "c:\\Program Files\\iTunes\\Plug-ins\\projectM\\presets" );
|
|
|
|
projectM_init( globalPM );
|
|
}
|
|
#else
|
|
#ifdef MACOS
|
|
void InitializeGL( CGrafPtr destPort, int isFullScreen ) {
|
|
|
|
AGLPixelFormat pixelFormat;
|
|
GLint attrib[64];
|
|
|
|
DWRITE( "InitializeGL(): in: %d\n", isFullScreen );
|
|
|
|
/** Stash the target port */
|
|
port = destPort;
|
|
|
|
#ifdef PANTS
|
|
if ( isFullScreen ) {
|
|
CGCaptureAllDisplays();
|
|
displayCaptured = 1;
|
|
if ( fullscreenMode != NULL ) {
|
|
if ( CGDisplaySwitchToMode( kCGDirectMainDisplay, fullscreenMode ) != CGDisplayNoErr ) {
|
|
DWRITE( "%s\n", "failed to switch display to fullscreen" );
|
|
} else {
|
|
DWRITE( "%s\n", "display switch to fullscreen ok\n" );
|
|
}
|
|
}
|
|
} else {
|
|
/** Switch back to the previous desktop mode */
|
|
if ( desktopMode != NULL ) {
|
|
if ( CGDisplaySwitchToMode( kCGDirectMainDisplay, desktopMode ) != CGDisplayNoErr ) {
|
|
DWRITE( "%s\n", "failed to switch display" );
|
|
} else {
|
|
DWRITE( "%s\n", "display switch OK" );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/** Chuck out the old context if one exists */
|
|
if ( context != NULL ) {
|
|
aglDestroyContext( context );
|
|
context = NULL;
|
|
}
|
|
|
|
/** Choose the OpenGL pixel format */
|
|
#ifdef PANTS
|
|
if ( isFullScreen ) {
|
|
attrib[0] = AGL_RGBA;
|
|
attrib[1] = AGL_DOUBLEBUFFER;
|
|
attrib[2] = AGL_PIXEL_SIZE;
|
|
attrib[3] = 32;
|
|
attrib[4] = AGL_FULLSCREEN;
|
|
attrib[5] = AGL_NO_RECOVERY;
|
|
attrib[6] = AGL_SINGLE_RENDERER;
|
|
attrib[6] = AGL_NONE;
|
|
} else {
|
|
#endif
|
|
attrib[0] = AGL_RGBA;
|
|
attrib[1] = AGL_DOUBLEBUFFER;
|
|
attrib[2] = AGL_ACCELERATED;
|
|
attrib[3] = AGL_PIXEL_SIZE;
|
|
attrib[4] = 32;
|
|
attrib[5] = AGL_NO_RECOVERY;
|
|
attrib[6] = AGL_SINGLE_RENDERER;
|
|
attrib[5] = AGL_NONE;
|
|
// }
|
|
|
|
GDHandle mainDevice = GetMainDevice();
|
|
pixelFormat = aglChoosePixelFormat( &mainDevice, 1, attrib );
|
|
if ( pixelFormat == 0 ) {
|
|
DWRITE( "failed to select pixel format\n" );
|
|
exit( 1 );
|
|
CleanupGL();
|
|
isFullScreen = 0;
|
|
return;
|
|
} else {
|
|
DWRITE( "selected pixel format OK\n" );
|
|
}
|
|
|
|
context = aglCreateContext( pixelFormat, NULL );
|
|
if ( context == NULL ) {
|
|
DWRITE( "failed to create context\n" );
|
|
} else {
|
|
DWRITE( "created context OK\n" );
|
|
}
|
|
|
|
// if ( !isFullScreen ) {
|
|
if ( !aglSetDrawable( context, destPort ) ) {
|
|
DWRITE( "failed to set drawable\n" );
|
|
} else {
|
|
DWRITE( "set drawable OK\n" );
|
|
}
|
|
// } else {
|
|
// aglSetDrawable( context, NULL );
|
|
// }
|
|
|
|
if ( !aglSetCurrentContext( context ) ) {
|
|
DWRITE( "failed to make context current\n" );
|
|
} else {
|
|
DWRITE( "set current context OK\n" );
|
|
}
|
|
|
|
// if ( !isFullScreen ) {
|
|
if ( !aglUpdateContext( context ) ) {
|
|
DWRITE( "failed to update context\n" );
|
|
} else {
|
|
DWRITE( "updated context OK\n" );
|
|
}
|
|
|
|
if ( globalPM != NULL ) {
|
|
globalPM->fullscreen = 0;
|
|
}
|
|
#ifdef PANTS
|
|
} else {
|
|
aglSetFullScreen( context, 800, 600, 0, 0 );
|
|
if ( globalPM != NULL ) {
|
|
globalPM->fullscreen = 1;
|
|
}
|
|
GLint displayCaps[3];
|
|
aglGetInteger( context, AGL_FULLSCREEN, displayCaps );
|
|
DWRITE( "dcaps: %d\t%d\t%d\n",
|
|
displayCaps[0], displayCaps[1], displayCaps[2] );
|
|
fullscreenWidth = displayCaps[0];
|
|
fullscreenHeight = displayCaps[1];
|
|
}
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
// enable v-sync if WGL_EXT_swap_control is supported
|
|
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
|
|
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
|
|
if (wglSwapIntervalEXT != NULL)
|
|
wglSwapIntervalEXT(1);
|
|
#endif
|
|
#ifdef MACOS
|
|
/** Setup VSYNC */
|
|
GLint sync = 1;
|
|
aglSetInteger ( context, AGL_SWAP_INTERVAL, &sync );
|
|
#endif
|
|
|
|
/** Initialise projectM */
|
|
globalPM->projectM_init();
|
|
}
|
|
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
|
|
// render visualization data
|
|
void RenderGL()
|
|
{
|
|
#ifdef WIN32
|
|
if (windowDC == NULL || globalPM == NULL ) return;
|
|
#else
|
|
#ifdef MACOS
|
|
if ( port == NULL || context == NULL ) {
|
|
return;
|
|
}
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
|
|
/** Stuff the PCM data into projectM */
|
|
globalPM->beatDetect->pcm->addPCM8( renderData.waveformData );
|
|
|
|
/** Render a frame via projectM */
|
|
globalPM->renderFrame();
|
|
|
|
#if defined(DEBUG2) && defined(MACOS)
|
|
fprintf( debugFile, "port: %X\tcontext: %X\n", port, context );
|
|
fflush( debugFile );
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
/** Buffer swap to display the results */
|
|
SwapBuffers( windowDC );
|
|
#else
|
|
#ifdef MACOS
|
|
aglSwapBuffers( context );
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
}
|
|
|
|
// resize rendering viewport
|
|
void ResizeGL( CGrafPtr destPort, Rect windowRect, int isFullScreen ) {
|
|
|
|
#ifdef MACOS
|
|
GLint bufferRect[4];
|
|
#endif
|
|
|
|
DWRITE( "ResizeGL(): in: %d\n", isFullScreen );
|
|
|
|
#ifdef PANTS
|
|
if ( isFullScreen ) {
|
|
#ifdef MACOS
|
|
aglDisable( context, AGL_BUFFER_RECT );
|
|
#endif /** MACOS */
|
|
|
|
/** Reset projectM */
|
|
if ( globalPM != NULL ) {
|
|
globalPM->projectM_resetGL( fullscreenWidth, fullscreenHeight );
|
|
}
|
|
} else {
|
|
#endif
|
|
windowWidth = windowRect.right - windowRect.left;
|
|
windowHeight = windowRect.bottom - windowRect.top;
|
|
|
|
if (windowHeight == 0) windowHeight = 1;
|
|
|
|
/** Re-initialise OpenGL */
|
|
if ( globalPM != NULL ) {
|
|
#ifdef MACOS
|
|
/**
|
|
* Grab the dimensions of the main window itself. This is required
|
|
* to accurately locate the port since OpenGL is "upside-down" from
|
|
* Carbon window coordinates...
|
|
*/
|
|
WindowRef itWindowRef;
|
|
Rect mwindowRect;
|
|
itWindowRef = GetWindowFromPort(port);
|
|
GetWindowPortBounds(itWindowRef, &mwindowRect);
|
|
|
|
DWRITE( "rect: (%d, %d) -> (%d, %d)\n",
|
|
windowRect.left, windowRect.top,
|
|
windowRect.right, windowRect.bottom );
|
|
DWRITE( "window-rect: (%d, %d) -> (%d, %d)\n",
|
|
mwindowRect.left, mwindowRect.top,
|
|
mwindowRect.right, mwindowRect.bottom );
|
|
|
|
/** Update the buffer rect */
|
|
bufferRect[0] = windowRect.left;
|
|
bufferRect[1] = mwindowRect.bottom - windowRect.bottom;
|
|
bufferRect[2] = windowWidth;
|
|
bufferRect[3] = windowHeight;
|
|
|
|
aglSetInteger( context, AGL_BUFFER_RECT, bufferRect );
|
|
aglEnable( context, AGL_BUFFER_RECT );
|
|
|
|
if ( !aglUpdateContext( context ) ) {
|
|
DWRITE( "failed to update context\n" );
|
|
} else {
|
|
DWRITE( "updated context OK\n" );
|
|
}
|
|
#endif
|
|
globalPM->projectM_resetGL( windowWidth, windowHeight );
|
|
}
|
|
// }
|
|
}
|
|
|
|
// cleanup OpenGL rendering context
|
|
void CleanupGL()
|
|
{
|
|
#ifdef WIN32
|
|
wglMakeCurrent(NULL, NULL);
|
|
|
|
if (windowRC != NULL)
|
|
{
|
|
wglDeleteContext(windowRC);
|
|
windowRC = NULL;
|
|
}
|
|
|
|
if (windowDC)
|
|
{
|
|
ReleaseDC(windowHandle, windowDC);
|
|
windowDC = NULL;
|
|
}
|
|
#else
|
|
#ifdef MACOS
|
|
/** Release the held main display */
|
|
#ifdef PANTS
|
|
if ( displayCaptured ) {
|
|
DWRITE( "%s\n", "switching to non-fullscreen" );
|
|
CGDisplaySwitchToMode( kCGDirectMainDisplay, desktopMode );
|
|
|
|
DWRITE( "%s\n", "releasing displays" );
|
|
CGReleaseAllDisplays();
|
|
}
|
|
#endif
|
|
DWRITE( "pre-context destroy\n" );
|
|
if ( context != NULL ) {
|
|
aglSetCurrentContext( NULL );
|
|
aglDestroyContext( context );
|
|
context = NULL;
|
|
}
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
}
|
|
|
|
// handle messages sent by iTunes
|
|
OSStatus pluginMessageHandler( OSType message,
|
|
VisualPluginMessageInfo *messageInfo,
|
|
void *refCon) {
|
|
|
|
OSStatus status = noErr;
|
|
|
|
switch (message) {
|
|
// sent when the plugin is loaded/unloaded
|
|
case kVisualPluginInitMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginInitMessage" );
|
|
break;
|
|
}
|
|
case kVisualPluginCleanupMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginCleanupMessage" );
|
|
// CleanupGL();
|
|
break;
|
|
}
|
|
// sent when plugin is enabled/disabled, plugin should handle these messages and do nothing
|
|
case kVisualPluginEnableMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginEnableMessage" );
|
|
break;
|
|
}
|
|
case kVisualPluginDisableMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginDisableMessage" );
|
|
break;
|
|
}
|
|
|
|
// redraw the screne while idle
|
|
case kVisualPluginIdleMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginIdleMessage" );
|
|
if (playing == false) {
|
|
RenderGL();
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
// sent when the visualizer is shown
|
|
case kVisualPluginShowWindowMessage: {
|
|
DWRITE( "*** kVisualPluginShowWindowMessage( %d )\n",
|
|
messageInfo->u.showWindowMessage.options );
|
|
#ifdef WIN32
|
|
InitializeGL(messageInfo->u.showWindowMessage.window,
|
|
messageInfo->u.showWindowMessage.options );
|
|
#else
|
|
#ifdef MACOS
|
|
InitializeGL( messageInfo->u.showWindowMessage.port,
|
|
/* messageInfo->u.showWindowMessage.options */ 0 );
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
ResizeGL( messageInfo->u.showWindowMessage.port,
|
|
messageInfo->u.setWindowMessage.drawRect,
|
|
/* messageInfo->u.setWindowMessage.options */ 0 );
|
|
RenderGL();
|
|
|
|
break;
|
|
}
|
|
|
|
// sent when the visualizer is hidden
|
|
case kVisualPluginHideWindowMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginHideWindowMessage" );
|
|
CleanupGL();
|
|
break;
|
|
}
|
|
|
|
#ifdef PANTS
|
|
// sent when visualizer viewport size is changed
|
|
case kVisualPluginSetWindowMessage: {
|
|
DWRITE( "*** kVisualPluginSetWindowMessage( %d )\n",
|
|
messageInfo->u.setWindowMessage.options );
|
|
ResizeGL(messageInfo->u.setWindowMessage.port,
|
|
messageInfo->u.setWindowMessage.drawRect,
|
|
messageInfo->u.setWindowMessage.options);
|
|
RenderGL();
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
// sent when visualizer should render a frame
|
|
case kVisualPluginRenderMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginRenderMessage" );
|
|
renderData = *messageInfo->u.renderMessage.renderData;
|
|
RenderGL();
|
|
|
|
break;
|
|
}
|
|
|
|
// sent when visualizer should update itself
|
|
case kVisualPluginUpdateMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginUpdateMessage" );
|
|
RenderGL();
|
|
break;
|
|
}
|
|
|
|
// sent when player is stopped or paused
|
|
case kVisualPluginStopMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginStopMessage" );
|
|
break;
|
|
}
|
|
case kVisualPluginPauseMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginPauseMessage" );
|
|
playing = false;
|
|
break;
|
|
}
|
|
|
|
// sent when player is started or unpaused
|
|
case kVisualPluginPlayMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginPlayMessage" );
|
|
break;
|
|
}
|
|
case kVisualPluginUnpauseMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginPauseMessage" );
|
|
/** Grab the track or stream title from iTunes */
|
|
if (messageInfo->u.changeTrackMessage.trackInfo != nil) {
|
|
trackInfo = *messageInfo->u.changeTrackMessage.trackInfo;
|
|
} else {
|
|
memset( &trackInfo, sizeof( trackInfo ) * sizeof( char ), 0 );
|
|
}
|
|
|
|
if (messageInfo->u.changeTrackMessage.streamInfo != nil) {
|
|
streamInfo = *messageInfo->u.changeTrackMessage.streamInfo;
|
|
} else {
|
|
memset( &streamInfo, sizeof( streamInfo ) * sizeof( char ), 0 );
|
|
}
|
|
|
|
/** Pass this info into projectM */
|
|
if ( globalPM != NULL ) {
|
|
globalPM->drawtitle = 1;
|
|
if ( strlen( (const char *)trackInfo.name ) > 0 ) {
|
|
globalPM->projectM_setTitle( (char *)trackInfo.name );
|
|
} else {
|
|
if ( strlen( (const char *)streamInfo.streamTitle ) > 0 ) {
|
|
globalPM->projectM_setTitle( (char *)streamInfo.streamTitle );
|
|
}
|
|
}
|
|
}
|
|
|
|
playing = true;
|
|
|
|
break;
|
|
}
|
|
|
|
/** Sent when the track changes */
|
|
case kVisualPluginChangeTrackMessage: {
|
|
DWRITE( "*** %s\n", "kVisualPluginChangeTrackMessage" );
|
|
/** Grab the track or stream title from iTunes */
|
|
if (messageInfo->u.changeTrackMessage.trackInfo != nil) {
|
|
trackInfo = *messageInfo->u.changeTrackMessage.trackInfo;
|
|
} else {
|
|
memset( &trackInfo, sizeof( trackInfo ) * sizeof( char ), 0 );
|
|
}
|
|
|
|
if (messageInfo->u.changeTrackMessage.streamInfo != nil) {
|
|
streamInfo = *messageInfo->u.changeTrackMessage.streamInfo;
|
|
} else {
|
|
memset( &streamInfo, sizeof( streamInfo ) * sizeof( char ), 0 );
|
|
}
|
|
|
|
/** Pass this info into projectM */
|
|
globalPM->drawtitle = 1;
|
|
if ( strlen( (const char *)trackInfo.name ) > 0 ) {
|
|
globalPM->projectM_setTitle( (char *)trackInfo.name );
|
|
} else {
|
|
if ( strlen( (const char *)streamInfo.streamTitle ) > 0 ) {
|
|
globalPM->projectM_setTitle( (char *)streamInfo.streamTitle );
|
|
}
|
|
}
|
|
RenderGL();
|
|
break;
|
|
}
|
|
case kVisualPluginEventMessage: {
|
|
projectMEvent event;
|
|
projectMKeycode keycode;
|
|
projectMModifier mod;
|
|
DWRITE( "*** %s\n", "kVisualPluginEventMessage" );
|
|
#ifdef MACOS
|
|
event = carbon2pmEvent( messageInfo->u.eventMessage.event );
|
|
keycode = carbon2pmKeycode( messageInfo->u.eventMessage.event );
|
|
#else
|
|
#ifdef WIN32
|
|
#endif /** WIN32 */
|
|
#endif /** MACOS */
|
|
DWRITE( "keycode: %d\n", keycode );
|
|
if ( keycode == ' ' ) {
|
|
status = unimpErr;
|
|
}
|
|
globalPM->key_handler( event, keycode, mod );
|
|
break;
|
|
}
|
|
default: {
|
|
status = unimpErr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
// register plugin with iTunes
|
|
OSStatus registerPlugin(PluginMessageInfo *messageInfo) {
|
|
|
|
OSStatus status;
|
|
|
|
// plugin constants
|
|
#ifdef WIN32
|
|
const char *pluginTitle = "projectM";
|
|
const UInt32 pluginCreator = '\?\?\?\?';
|
|
#else
|
|
#ifdef MACOS
|
|
const char *pluginTitle = "projectM";
|
|
const UInt32 pluginCreator = 'hook';
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
const UInt8 pluginMajorVersion = 10;
|
|
const UInt8 pluginMinorVersion = 9;
|
|
|
|
PlayerMessageInfo playerMessageInfo;
|
|
memset( &playerMessageInfo.u.registerVisualPluginMessage,
|
|
0, sizeof(playerMessageInfo.u.registerVisualPluginMessage));
|
|
|
|
// copy in name length byte first
|
|
playerMessageInfo.u.registerVisualPluginMessage.name[0] =
|
|
(UInt8)strlen( pluginTitle );
|
|
|
|
// now copy in actual name
|
|
memcpy( &playerMessageInfo.u.registerVisualPluginMessage.name[1],
|
|
pluginTitle, strlen( pluginTitle ));
|
|
|
|
SetNumVersion( &playerMessageInfo.u.registerVisualPluginMessage.pluginVersion,
|
|
pluginMajorVersion, pluginMinorVersion, 0x80, 0);
|
|
|
|
playerMessageInfo.u.registerVisualPluginMessage.options =
|
|
kVisualWantsIdleMessages;
|
|
#ifdef WIN32
|
|
playerMessageInfo.u.registerVisualPluginMessage.handler =
|
|
pluginMessageHandler;
|
|
#else
|
|
playerMessageInfo.u.registerVisualPluginMessage.handler =
|
|
(VisualPluginProcPtr)pluginMessageHandler;
|
|
#endif
|
|
playerMessageInfo.u.registerVisualPluginMessage.registerRefCon = 0;
|
|
playerMessageInfo.u.registerVisualPluginMessage.creator = pluginCreator;
|
|
|
|
playerMessageInfo.u.registerVisualPluginMessage.timeBetweenDataInMS = 0xFFFFFFFF; // 16 milliseconds = 1 Tick, 0xFFFFFFFF = Often as possible.
|
|
playerMessageInfo.u.registerVisualPluginMessage.numWaveformChannels = 2;
|
|
playerMessageInfo.u.registerVisualPluginMessage.numSpectrumChannels = 2;
|
|
|
|
playerMessageInfo.u.registerVisualPluginMessage.minWidth = 64;
|
|
playerMessageInfo.u.registerVisualPluginMessage.minHeight = 64;
|
|
playerMessageInfo.u.registerVisualPluginMessage.maxWidth = 32767;
|
|
playerMessageInfo.u.registerVisualPluginMessage.maxHeight = 32767;
|
|
playerMessageInfo.u.registerVisualPluginMessage.minFullScreenBitDepth = 0;
|
|
playerMessageInfo.u.registerVisualPluginMessage.maxFullScreenBitDepth = 0;
|
|
playerMessageInfo.u.registerVisualPluginMessage.windowAlignmentInBytes = 0;
|
|
|
|
status = PlayerRegisterVisualPlugin(messageInfo->u.initMessage.appCookie,
|
|
messageInfo->u.initMessage.appProc,
|
|
&playerMessageInfo);
|
|
|
|
appCookie = messageInfo->u.initMessage.appCookie;
|
|
appProc = messageInfo->u.initMessage.appProc;
|
|
|
|
return status;
|
|
}
|
|
|
|
#ifdef WIN32
|
|
#define IMPEXP __declspec(dllexport)
|
|
#define MAIN iTunesPluginMain
|
|
#else
|
|
#ifdef MACOS
|
|
#define IMPEXP
|
|
#define MAIN iTunesPluginMainMachO
|
|
#endif /** MACOS */
|
|
#endif /** WIN32 */
|
|
|
|
#ifdef MACOS
|
|
extern "C"
|
|
#endif
|
|
IMPEXP OSStatus MAIN (OSType message, PluginMessageInfo *messageInfo, void *refCon)
|
|
{
|
|
OSStatus status = noErr;
|
|
char fname[1024];
|
|
char fontURL[1024];
|
|
char presetURL[1024];
|
|
|
|
switch (message) {
|
|
case kPluginInitMessage: {
|
|
#ifdef DEBUG
|
|
#ifdef MACOS
|
|
debugFile = fopen( "/Users/descarte/iprojectM.txt", "wb" );
|
|
fprintf( debugFile, "registerPlugin: %d\n", status );
|
|
fflush( debugFile );
|
|
#endif /** MACOS */
|
|
#endif /** DEBUG */
|
|
|
|
DWRITE( "%s: %d\t%d\n",
|
|
"kPluginInitMessage",
|
|
messageInfo->u.initMessage.majorVersion,
|
|
messageInfo->u.initMessage.minorVersion );
|
|
status = registerPlugin(messageInfo);
|
|
if ( status == noErr ) {
|
|
#ifdef MACOS
|
|
desktopMode = CGDisplayCurrentMode( kCGDirectMainDisplay );
|
|
fullscreenMode =
|
|
CGDisplayBestModeForParameters( kCGDirectMainDisplay,
|
|
32, 800, 600, NULL );
|
|
#ifdef DEBUG
|
|
if ( debugFile != NULL ) {
|
|
long dwidth = 0, dheight = 0;
|
|
/** Get the desktop mode */
|
|
/* Get the height, width, bpp, and refresh rate of this display mode. Print them out. */
|
|
CFNumberRef number ;
|
|
number = (CFNumberRef)CFDictionaryGetValue( desktopMode, kCGDisplayWidth ) ;
|
|
CFNumberGetValue( number, kCFNumberLongType, &dwidth ) ;
|
|
number = (CFNumberRef)CFDictionaryGetValue( desktopMode, kCGDisplayHeight ) ;
|
|
CFNumberGetValue( number, kCFNumberLongType, &dheight ) ;
|
|
|
|
DWRITE( "stashing desktop mode: %d x %d -> %d\n",
|
|
dwidth, dheight, exactMatch );
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/** Initialise projectM */
|
|
globalPM = new projectM();
|
|
globalPM->projectM_reset();
|
|
|
|
/** Set basic rendering options */
|
|
globalPM->renderTarget->usePbuffers = 1;
|
|
globalPM->fullscreen = 0;
|
|
globalPM->renderTarget->texsize = 1024;
|
|
globalPM->showfps = 1;
|
|
|
|
/** Get the font and preset locations */
|
|
PlayerGetPluginFileSpec( appCookie, appProc, &pluginSpec );
|
|
fss2path( &pluginSpec, fname );
|
|
DWRITE( "fsspace name: %s\n", fname );
|
|
sprintf( fontURL, "%s/Contents/Resources/fonts", fname );
|
|
globalPM->fontURL = (char *)malloc( sizeof( char ) * 1024 );
|
|
strncpy( globalPM->fontURL, fontURL, strlen( fontURL ) + 1 );
|
|
|
|
DWRITE( "fontURL: %s\n", globalPM->fontURL );
|
|
|
|
sprintf( presetURL, "%s/Contents/Resources/presets", fname );
|
|
globalPM->presetURL = (char *)malloc( sizeof( char ) * 1024 );
|
|
strncpy( globalPM->presetURL, presetURL, strlen( presetURL ) + 1 );
|
|
|
|
DWRITE( "presetURL: %s\n", globalPM->presetURL );
|
|
}
|
|
|
|
break;
|
|
}
|
|
case kPluginCleanupMessage: {
|
|
DWRITE( "%s\n", "kPluginCleanupMessage" );
|
|
#ifdef DEBUG
|
|
if ( debugFile != NULL ) {
|
|
fclose( debugFile );
|
|
}
|
|
#endif
|
|
status = noErr;
|
|
break;
|
|
}
|
|
default: {
|
|
status = unimpErr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DWRITE( "status: %d\n", status );
|
|
|
|
return status;
|
|
}
|