/* _______ ____ __ ___ ___ * \ _ \ \ / \ / \ \ / / ' ' ' * | | \ \ | | || | \/ | . . * | | | | | | || ||\ /| | * | | | | | | || || \/ | | ' ' ' * | | | | | | || || | | . . * | |_/ / \ \__// || | | * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque * / \ * / . \ * in_duh.c - Winamp plug-in for DUMB. / / \ \ * | < / \_ * By Bob. | \/ /\ / * \_ / > / * | \ / / * | ' / * \__/ */ //#define SOFTVOLUME #include "in_duh.h" #include "resource.h" #include "gui.h" typedef struct DUH_PLAYER { int n_channels; DUH_SIGRENDERER *dr; float volume; } DUH_PLAYER; /* Winamp Output module; we write to this to tell Winamp what to play */ In_Module mod; /* Currently playing file */ DUH *duh; DUH_PLAYER *duh_player; int init_duh = TRUE; char *duh_filename = NULL; #ifdef SOFTVOLUME int thevolume = 255; #endif /****************** * Configuration */ static int bits_per_sample; static int frequency; static int stereo; static int resampling; static int buffer_size; static int thread_priority; /**************** * Winamp Stuff */ HANDLE input_file = INVALID_HANDLE_VALUE; /* 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 */ DWORD WINAPI __stdcall DecodeThread(void *b); /* the decode thread procedure */ /* Avoid CRT. Evil. Big. Bloated. */ BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { (void)hInst; (void)ul_reason_for_call; (void)lpReserved; return TRUE; } /* Post this to the main window at end of file (after playback as stopped) */ #define WM_WA_MPEG_EOF (WM_USER+2) /* Stuff for interfacing with Winamp */ int decode_pos_ms; int paused; int seek_needed; /* if != -1, it is the point that the decode thread should seek to, in ms. */ //char *sample_buffer = NULL; //int buffer_pos = 0; /* Init DUH */ void init() { config_init(); dumb_register_stdfiles(); } /* De-Init */ void quit() { config_quit(); if (duh_player) free(duh_player); if (duh) unload_duh(duh); if (duh_filename) free(duh_filename); //if (sample_buffer) //free(sample_buffer); dumb_exit(); } /* WA SDK: used for detecting URL streams.. unused here. strncmp(fn,"http://",7) to detect HTTP streams, etc */ /* I -think- we need to tell Winamp that the file should use our plug-in */ int isourfile(char *fn) { (void)fn; return 0; } void stop_duh(DUH_PLAYER *dp) { if (dp) { duh_end_sigrenderer(dp->dr); free(dp); } } int play(char *fn) { static int priority_table[] = { THREAD_PRIORITY_NORMAL, THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST }; int maxlatency; unsigned long thread_id; /* Get rid of an old DUH */ if (duh_player) { stop_duh(duh_player); duh_player = NULL; } if (duh) unload_duh(duh); /* Load file */ duh = load_duh(fn); if (!duh) { duh = dumb_load_it(fn); if (!duh) { duh = dumb_load_xm(fn); if (!duh) { duh = dumb_load_s3m(fn); if (!duh) { duh = dumb_load_mod(fn); if (!duh) return 1; } } } } init_duh = TRUE; /* Set up some things for Winamp */ paused = 0; decode_pos_ms = 0; seek_needed = -1; bits_per_sample = config_bits_per_sample; frequency = config_frequency; stereo = config_stereo; resampling = config_resampling; buffer_size = config_buffer_size; thread_priority = priority_table[config_thread_priority]; /* Create the sample buffer */ //if (sample_buffer) //free(sample_buffer); //sample_buffer = malloc(((bits_per_sample + 7) / 8) * stereo * buffer_size); //if (!sample_buffer) //return 1; //buffer_pos = 0; /* Note: I -really- don't know what this does. Winamp's SDK doesn't mention this function... */ maxlatency = mod.outMod->Open(frequency, stereo, bits_per_sample, -1, -1); if (maxlatency < 0) { /* error opening device */ unload_duh(duh); duh = NULL; return 1; } /* Store the file name */ if (duh_filename) free(duh_filename); duh_filename = strdup(fn); /* Note2: Dunno what does too; damn those Winamp docs */ /* dividing by 1000 for the first parameter of setinfo makes it */ /* display 'H'... for hundred.. i.e. 14H Kbps. */ mod.SetInfo((frequency * bits_per_sample * stereo) / 1000, frequency / 1000, stereo, 1); /* Ditto */ /* initialize vis stuff */ mod.SAVSAInit(maxlatency, frequency); mod.VSASetInfo(frequency, stereo); /* Ditthree */ #ifdef SOFTVOLUME mod.outMod->SetVolume(255); #else mod.outMod->SetVolume(-666); /* set the output plug-ins default volume */ #endif /* Ok, now we set up the decoding thread */ killDecodeThread = 0; thread_handle = (HANDLE) CreateThread(NULL,0,DecodeThread,&killDecodeThread,0,&thread_id); SetThreadPriority(thread_handle, thread_priority); return 0; } /* Standard Winamp stuff */ void pause() { paused = 1; mod.outMod->Pause(1); } void unpause() { paused = 0; mod.outMod->Pause(0); } int ispaused() { return paused; } /* Stop playing the file */ void stop() { 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); } CloseHandle(thread_handle); thread_handle = INVALID_HANDLE_VALUE; } if (duh_player) { stop_duh(duh_player); duh_player = NULL; } if (duh) { unload_duh(duh); duh = NULL; } /* Should I unload the file? It takes time to reload... */ mod.outMod->Close(); mod.SAVSADeInit(); } int getlength() { return (int) ((LONG_LONG)duh_get_length(duh) * 1000 >> 16); } int getoutputtime() { return decode_pos_ms + (mod.outMod->GetOutputTime() - mod.outMod->GetWrittenTime()); } void setoutputtime(int time_in_ms) { seek_needed = time_in_ms; } void setvolume(int volume) { #ifdef SOFTVOLUME thevolume = volume; if (duh_player) duh_player->volume = volume / 255.0f; #else mod.outMod->SetVolume(volume); #endif } void setpan(int pan) { mod.outMod->SetPan(pan); } int infoDlg(char *fn, HWND hwnd) { (void)fn; (void)hwnd; // TODO: implement info dialog. return 0; } static const char *fn_basename(const char *filename) { for (;;) { const char *p = strpbrk(filename, "/\\"); if (!p) return filename; filename = p + 1; } } void getfileinfo(char *filename, char *title, int *length_in_ms) { if (!filename || !*filename) { /* currently playing file */ if (length_in_ms) *length_in_ms = getlength(); if (title) { const char *mod_title = duh_get_tag(duh, "TITLE"); if (mod_title && mod_title[0]) sprintf(title, "%s - %s", fn_basename(filename), mod_title); else strcpy(title, fn_basename(filename)); } } else { /* some other file */ #if 1 // needs fixing better than this! more to add to DUMB's API? if (length_in_ms || title) { DUH *duh = load_duh(filename); if (!duh) { duh = dumb_load_it(filename); if (!duh) { duh = dumb_load_xm(filename); if (!duh) { duh = dumb_load_s3m(filename); if (!duh) { duh = dumb_load_mod(filename); if (!duh) return; } } } } if (length_in_ms) *length_in_ms = (int)((LONG_LONG)duh_get_length(duh) * 1000 >> 16); if (title) { const char *mod_title = duh_get_tag(duh, "TITLE"); if (mod_title && mod_title[0]) sprintf(title, "%s - %s", fn_basename(duh_filename), mod_title); else strcpy(title, fn_basename(duh_filename)); } unload_duh(duh); } #elif 0 /* This code only works (worked?) for DUH files. */ DUMBFILE *d = dumbfile_open(filename); if (!d) return; if (dumbfile_mgetl(d) != DUH_SIGNATURE) return; *length_in_ms = (unsigned int)((LONG_LONG)dumbfile_igetl(d) * 1000 >> 16); if (title) strcpy(title, filename); dumbfile_close(d); #else *length_in_ms = 1000 * 60 * 10; if (title) strcpy(title, filename); #endif } } void eq_set(int on, char data[10], int preamp) { (void)on; (void)data; (void)preamp; /* No equalizer support, sorry */ } DUH_PLAYER *start_duh(DUH *duh, int n_channels, long pos, float volume) { DUH_PLAYER *dp; // This restriction is imposed by Allegro. <-- um...? ASSERT(n_channels > 0); ASSERT(n_channels <= 2); if (!duh) return NULL; dp = malloc(sizeof(*dp)); if (!dp) return NULL; dp->n_channels = n_channels; dp->dr = duh_start_sigrenderer(duh, 0, n_channels, pos); if (!dp->dr) { free(dp); return NULL; } { DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(dp->dr); dumb_it_set_loop_callback(itsr, &dumb_it_callback_terminate, NULL); dumb_it_set_xm_speed_zero_callback(itsr, &dumb_it_callback_terminate, NULL); } dp->volume = volume; return dp; } void pause_duh(DUH_PLAYER *dp) { (void)dp; pause(); } void resume_duh(DUH_PLAYER *dp) { (void)dp; unpause(); } void set_duh_volume(DUH_PLAYER *dp, float volume) { (void)dp; setvolume((int)(volume * 255.0f)); } /* Generate 576 samples of data from the DUH_PLAYER */ int get_576_samples(DUH_PLAYER *dp, char *buf) { #if 1 if (!dp) return 0; long n = duh_render(dp->dr, bits_per_sample, bits_per_sample == 8 ? 1 : 0, dp->volume, 65536.0f / frequency, 576, buf); return n * (bits_per_sample >> 3) * stereo; #else long n; int bps = ((bits_per_sample + 7) / 8) * stereo; if (!dp) return 0; if (buffer_pos == 0 || buffer_pos + 576 >= buffer_size) { if (buffer_pos) { memmove(sample_buffer, sample_buffer + bps * buffer_pos, (buffer_size - buffer_pos) * bps); buffer_pos = buffer_size - buffer_pos; } n = duh_render(dp->dr, bits_per_sample, bits_per_sample == 8 ? 1 : 0, dp->volume, 65536.0f / frequency, buffer_size - buffer_pos, sample_buffer + buffer_pos * bps); if (n > 576) n = 576; n *= bps; buffer_pos = 0; } memcpy(buf, sample_buffer + buffer_pos * bps, 576 * bps); buffer_pos += 576; return 576 * bps; #endif } DWORD WINAPI __stdcall DecodeThread(void *b) { static char buf[576 * 4]; int done = 0; int length = 0; if (init_duh) { dumb_resampling_quality = resampling; #ifdef SOFTVOLUME duh_player = start_duh(duh, stereo, 0, thevolume / 255.0f); #else duh_player = start_duh(duh, stereo, 0, 1.0f); #endif init_duh = FALSE; } length = getlength(); while (! *((int *)b) ) { if (seek_needed != -1) { decode_pos_ms = seek_needed-(seek_needed%1000); seek_needed = -1; done = 0; mod.outMod->Flush(decode_pos_ms); /* Position the playback pointer */ stop_duh(duh_player); duh_player = start_duh(duh, stereo, (unsigned int)(decode_pos_ms * 65536.0 / 1000.0), 1.0); } if (done) { mod.outMod->CanWrite(); if (!mod.outMod->IsPlaying()) { PostMessage(mod.hMainWindow,WM_WA_MPEG_EOF,0,0); return 0; } Sleep(10); } else if (mod.outMod->CanWrite() >= ((576 * stereo * ((bits_per_sample + 7) / 8)))) { int l = get_576_samples(duh_player, buf); if (!l || decode_pos_ms >= length) { done = 1; } else { /* Vis plug-ins interface */ mod.SAAddPCMData((char *)buf, stereo, bits_per_sample, decode_pos_ms); mod.VSAAddPCMData((char *)buf, stereo, bits_per_sample, decode_pos_ms); /* Add PCM to output buffer */ decode_pos_ms += (576 * 1000) / frequency; if (mod.dsp_isactive()) l = mod.dsp_dosamples((short *)buf, l / stereo / ((bits_per_sample + 7) / 8), bits_per_sample, stereo, frequency) * (stereo * ((bits_per_sample + 7) / 8)); mod.outMod->Write(buf, l); } } else /* Nothing to do this pass */ Sleep(config_frequency / 1000); } return 0; } In_Module mod = { IN_VER, "DUH! Player v" VERSION #ifdef __alpha " (AXP)" #else " (x86)" #endif , 0, /* hMainWindow */ 0, /* hDllInstance */ "DUH\0Dynamic Universal Harmony File (*.DUH)\0" "IT\0Impulse Tracker Module (*.IT)\0" "XM\0Fast Tracker 2 Module (*.XM)\0" "S3M\0Scream Tracker 3 Module (*.S3M)\0" "MOD\0Amiga Module (*.MOD)\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; }