/* ** .WV input plug-in for WavPack ** Copyright (c) 2000 - 2006, Conifer Software, All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include "in2.h" #include "wavpack.h" #include "resource.h" #include "wasabi/winamp/wa_ipc.h" #include "wasabi/wasabi.h" #include "wasabi/nu/autochar.h" #include "wasabi/nu/autowide.h" #define fileno _fileno static float calculate_gain(WavpackContext *wpc, bool allowDefault=true); #define PLUGIN_VERSION "2.8.1" //#define DEBUG_CONSOLE //#define ANSI_METADATA #define UNICODE_METADATA //#define OLD_INFO_DIALOG // post this to the main window at end of file (after playback as stopped) #define WM_WA_MPEG_EOF WM_USER+2 #define MAX_NCH 8 static struct wpcnxt { WavpackContext *wpc; // WavPack context provided by library float play_gain; // playback gain (for replaygain support) //int soft_clipping; // soft clipping active for playback int output_bits; // 16, 24, or 32 bits / sample long sample_buffer[576*MAX_NCH*2]; // sample buffer float error [MAX_NCH]; // error term for noise shaping char lastfn[MAX_PATH]; // filename stored for comparisons only wchar_t w_lastfn[MAX_PATH]; // w_filename stored for comparisons only FILE *wv_id, *wvc_id; // file pointer when we use reader callbacks } curr, edit, info; int 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. #define ALLOW_WVC 0x1 #define REPLAYGAIN_TRACK 0x2 #define REPLAYGAIN_ALBUM 0x4 #define SOFTEN_CLIPPING 0x8 #define PREVENT_CLIPPING 0x10 #define ALWAYS_16BIT 0x20 // new flags added for version 2.5 #define ALLOW_MULTICHANNEL 0x40 #define REPLAYGAIN_24BIT 0x80 int config_bits = ALLOW_WVC | ALLOW_MULTICHANNEL; // all configuration goes here 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 static api_service* WASABI_API_SVC; static api_language* WASABI_API_LNG; static api_config *AGAVE_API_CONFIG; static api_memmgr *WASABI_API_MEMMGR; HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; static char file_extensions[128] = {"WV\0WavPack Files (*.WV)\0"}; // function definitions for the In_Module stucture void about (HWND hwndParent); void init(); void quit(); void getfileinfo(const char *filename, char *title, int *length_in_ms); int infoDlg(const char *fn, HWND hwnd); int isourfile(const char *fn); int play(const char *fn); void pause(); void unpause(); int ispaused(); void stop(); int getlength(); int getoutputtime(); void setoutputtime(int time_in_ms); void setvolume(int volume); void setpan(int pan); void eq_set(int on, char data [10], int preamp); In_Module mod = // the output module { IN_VER, "WavPack Decoder v"PLUGIN_VERSION, 0, // hMainWindow 0, // hDllInstance file_extensions, 1, // is_seekable 1, // uses output about, 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 }; static BOOL CALLBACK WavPackDlgProc(HWND, UINT, WPARAM, LPARAM); extern long dump_alloc (void); int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message) { MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMSW),0}; msgbx.lpszText = message; msgbx.lpszCaption = title; msgbx.lpszIcon = MAKEINTRESOURCEW(102); msgbx.hInstance = GetModuleHandle(0); msgbx.dwStyle = MB_USERICON; msgbx.hwndOwner = parent; return MessageBoxIndirectW(&msgbx); } void about(HWND hwndParent) { wchar_t about_string[512]; #ifdef DEBUG_ALLOC sprintf (about_string, "alloc_count = %d", dump_alloc ()); #else StringCchPrintfW(about_string, 512, WASABI_API_LNGSTRINGW(IDS_ABOUT_MESSAGE), PLUGIN_VERSION, "1998-2010", __DATE__); #endif DoAboutMessageBox(hwndParent, WASABI_API_LNGSTRINGW(IDS_ABOUT), about_string); } void init() /* any one-time initialization goes here (configuration reading, etc) */ { if (mod.hMainWindow) { // load all of the required wasabi services from the winamp client WASABI_API_SVC = (api_service *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_API_SERVICE); if (WASABI_API_SVC == (api_service *)1) WASABI_API_SVC=0; WASABI_API_SVC->service_register(&albumArtFactory); } ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID); ServiceBuild(WASABI_API_LNG, languageApiGUID); ServiceBuild(WASABI_API_MEMMGR, memMgrApiServiceGuid); // need to have this initialised before we try to do anything with localisation features WASABI_API_START_LANG(mod.hDllInstance,InWvLangGuid); static char szDescription[256]; StringCchPrintfA(szDescription,256,WASABI_API_LNGSTRING(IDS_DESCRIPTION),PLUGIN_VERSION); mod.description = szDescription; // set the file extension to the localised version char tmp [64], *tmp_ptr = tmp, *fex_ptr = file_extensions; WASABI_API_LNGSTRING_BUF(IDS_FILETYPE, tmp, sizeof (tmp)); *fex_ptr++ = 'W'; *fex_ptr++ = 'V'; *fex_ptr++ = 0; while (*tmp_ptr) *fex_ptr++ = *tmp_ptr++; *fex_ptr++ = 0; *fex_ptr++ = 0; } #ifdef DEBUG_CONSOLE HANDLE debug_console=INVALID_HANDLE_VALUE; // debug console void debug_write (char *str) { static int cant_debug; if (cant_debug) return; if (debug_console == INVALID_HANDLE_VALUE) { AllocConsole (); #if 1 debug_console = GetStdHandle (STD_OUTPUT_HANDLE); #else debug_console = CreateConsoleScreenBuffer (GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); #endif if (debug_console == INVALID_HANDLE_VALUE) { MessageBox(NULL, "Can't get a console handle", "WavPack",MB_OK); cant_debug = 1; return; } else if (!SetConsoleActiveScreenBuffer (debug_console)) { MessageBox(NULL, "Can't activate console buffer", "WavPack",MB_OK); cant_debug = 1; return; } } WriteConsole (debug_console, str, strlen (str), NULL, NULL); } #endif void quit() /* one-time deinit, such as memory freeing */ { #ifdef DEBUG_CONSOLE if (debug_console != INVALID_HANDLE_VALUE) { FreeConsole (); if (debug_console != GetStdHandle (STD_OUTPUT_HANDLE)) CloseHandle (debug_console); debug_console = INVALID_HANDLE_VALUE; } #endif ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID); ServiceRelease(WASABI_API_LNG, languageApiGUID); ServiceRelease(WASABI_API_MEMMGR, memMgrApiServiceGuid); WASABI_API_SVC->service_deregister(&albumArtFactory); } // used for detecting URL streams.. unused here. strncmp(fn,"http://",7) to detect HTTP streams, etc int isourfile(const char *fn) { return 0; } int play(const char *fn) { int num_chans, sample_rate; char error[128]; int maxlatency; int thread_id; int open_flags; #ifdef DEBUG_CONSOLE sprintf (error, "play (%s)\n", fn); debug_write (error); #endif open_flags = OPEN_TAGS | OPEN_NORMALIZE; if (config_bits & ALLOW_WVC) open_flags |= OPEN_WVC; if (!(config_bits & ALLOW_MULTICHANNEL)) open_flags |= OPEN_2CH_MAX; curr.wpc = WavpackOpenFileInput(fn, error, open_flags, 0); if (!curr.wpc) // error opening file, just return error return -1; num_chans = WavpackGetReducedChannels(curr.wpc); sample_rate = WavpackGetSampleRate(curr.wpc); curr.output_bits = WavpackGetBitsPerSample(curr.wpc) > 16 ? 24 : 16; if (config_bits & ALWAYS_16BIT) curr.output_bits = 16; else if ((config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM)) && (config_bits & REPLAYGAIN_24BIT)) curr.output_bits = 24; if (num_chans > MAX_NCH) // don't allow too many channels! { WavpackCloseFile(curr.wpc); return -1; } curr.play_gain = calculate_gain(curr.wpc); lstrcpyn(curr.lastfn, fn, MAX_PATH); paused = 0; decode_pos_ms = 0; seek_needed = -1; maxlatency = mod.outMod->Open(sample_rate, num_chans, curr.output_bits, -1, -1); if (maxlatency < 0) // error opening device { curr.wpc = WavpackCloseFile(curr.wpc); return -1; } // dividing by 1000 for the first parameter of setinfo makes it // display 'H'... for hundred.. i.e. 14H Kbps. mod.SetInfo(0, (sample_rate + 500) / 1000, num_chans, 1); // initialize vis stuff mod.SAVSAInit(maxlatency, sample_rate); mod.VSASetInfo(sample_rate, num_chans); mod.outMod->SetVolume(-666); // set the output plug-ins default volume killDecodeThread=0; thread_handle = (HANDLE)CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) DecodeThread, (void *) &killDecodeThread, 0, (LPDWORD)&thread_id); if (SetThreadPriority(thread_handle, THREAD_PRIORITY_HIGHEST) == 0) { curr.wpc = WavpackCloseFile(curr.wpc); return -1; } return 0; } void pause() { #ifdef DEBUG_CONSOLE debug_write ("pause ()\n"); #endif paused = 1; mod.outMod->Pause(1); } void unpause() { #ifdef DEBUG_CONSOLE debug_write ("unpause ()\n"); #endif paused = 0; mod.outMod->Pause(0); } int ispaused() { return paused; } void stop() { #ifdef DEBUG_CONSOLE debug_write ("stop ()\n"); #endif 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 (curr.wpc) curr.wpc = WavpackCloseFile(curr.wpc); mod.outMod->Close(); mod.SAVSADeInit(); } int getlength() { return (int)(WavpackGetNumSamples (curr.wpc) * 1000.0 / WavpackGetSampleRate (curr.wpc)); } int getoutputtime() { if (seek_needed == -1) return decode_pos_ms + (mod.outMod->GetOutputTime () - mod.outMod->GetWrittenTime ()); else return seek_needed; } void setoutputtime (int time_in_ms) { #ifdef DEBUG_CONSOLE char str [40]; sprintf (str, "setoutputtime (%d)\n", time_in_ms); debug_write (str); #endif seek_needed = time_in_ms; } void setvolume (int volume) { mod.outMod->SetVolume(volume); } void setpan (int pan) { mod.outMod->SetPan(pan); } static void generate_format_string(WavpackContext *wpc, wchar_t *string, int maxlen, int wide); static void AnsiToUTF8(char *string, int len); static int UTF8ToWideChar(const char *pUTF8, wchar_t *pWide); static void UTF8ToAnsi(char *string, int len); int infoDlg(const char *fn, HWND hwnd) { #ifdef OLD_INFO_DIALOG char string[2048]; wchar_t w_string[2048]; WavpackContext *wpc; int open_flags; open_flags = OPEN_TAGS | OPEN_NORMALIZE; if (config_bits & ALLOW_WVC) open_flags |= OPEN_WVC; if (!(config_bits & ALLOW_MULTICHANNEL)) open_flags |= OPEN_2CH_MAX; wpc = WavpackOpenFileInput(fn, string, open_flags, 0); if (wpc) { int mode = WavpackGetMode(wpc); //generate_format_string(wpc, string, sizeof (string), 1); wchar_t *temp = (wchar_t *)malloc(sizeof(string) * sizeof(wchar_t)); generate_format_string(wpc, temp, sizeof(string), 0); lstrcpyn(string, AutoChar(temp, CP_UTF8), sizeof(string)); free(temp); if (WavpackGetMode(wpc) & MODE_VALID_TAG) { char value [128]; if (config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM)) { int local_clipping = 0; float local_gain; local_gain = calculate_gain(wpc); if (local_gain != 1.0) StringCchPrintf(string + strlen (string), 2048, "Gain: %+.2f dB %s\n", log10 (local_gain) * 20.0, local_clipping ? "(w/soft clipping)" : ""); } if (WavpackGetTagItem(wpc, "title", value, sizeof (value))) { if (!(mode & MODE_APETAG)) AnsiToUTF8(value, sizeof (value)); StringCchPrintf(string + strlen (string), 2048, "\nTitle: %s", value); } if (WavpackGetTagItem(wpc, "artist", value, sizeof (value))) { if (!(mode & MODE_APETAG)) AnsiToUTF8(value, sizeof (value)); StringCchPrintf(string + strlen (string), 2048, "\nArtist: %s", value); } if (WavpackGetTagItem (wpc, "album", value, sizeof (value))) { if (!(mode & MODE_APETAG)) AnsiToUTF8(value, sizeof (value)); StringCchPrintf (string + strlen (string), 2048, "\nAlbum: %s", value); } if (WavpackGetTagItem (wpc, "genre", value, sizeof (value))) { if (!(mode & MODE_APETAG)) AnsiToUTF8(value, sizeof (value)); StringCchPrintf(string + strlen (string), 2048, "\nGenre: %s", value); } if (WavpackGetTagItem (wpc, "comment", value, sizeof (value))) { if (!(mode & MODE_APETAG)) AnsiToUTF8(value, sizeof (value)); StringCchPrintf(string + strlen (string), 2048, "\nComment: %s", value); } if (WavpackGetTagItem(wpc, "year", value, sizeof (value))) StringCchPrintf(string + strlen (string), 2048, "\nYear: %s", value); if (WavpackGetTagItem(wpc, "track", value, sizeof (value))) StringCchPrintf(string + strlen (string), 2048, "\nTrack: %s", value); StringCchCat(string, 2048, "\n"); } UTF8ToWideChar(string, w_string); MessageBoxW(hwnd, w_string, L"WavPack File Info Box", MB_OK); wpc = WavpackCloseFile(wpc); } else MessageBox(hwnd, string, "WavPack Decoder", MB_OK); return 0; #else return 1; #endif } void getfileinfo(const char *filename, char *title, int *length_in_ms) { if (!filename || !*filename) // currently playing file { if (length_in_ms) *length_in_ms = getlength (); if (title) { if (WavpackGetTagItem(curr.wpc, "title", NULL, 0)) { char art [128], ttl [128]; WavpackGetTagItem(curr.wpc, "title", ttl, sizeof (ttl)); if (WavpackGetMode(curr.wpc) & MODE_APETAG) UTF8ToAnsi(ttl, sizeof (ttl)); if (WavpackGetTagItem(curr.wpc, "artist", art, sizeof (art))) { if (WavpackGetMode(curr.wpc) & MODE_APETAG) UTF8ToAnsi(art, sizeof (art)); StringCchPrintf(title, GETFILEINFO_TITLE_LENGTH, "%s - %s", art, ttl); } else lstrcpyn(title, ttl, GETFILEINFO_TITLE_LENGTH); } else { char *p = curr.lastfn + strlen (curr.lastfn); while (*p != '\\' && p >= curr.lastfn) p--; lstrcpyn(title, ++p, GETFILEINFO_TITLE_LENGTH); } } } else // some other file { WavpackContext *wpc; char error [128]; int open_flags; if (length_in_ms) *length_in_ms = -1000; if (title) *title = 0; open_flags = OPEN_TAGS | OPEN_NORMALIZE; if (config_bits & ALLOW_WVC) open_flags |= OPEN_WVC; if (!(config_bits & ALLOW_MULTICHANNEL)) open_flags |= OPEN_2CH_MAX; wpc = WavpackOpenFileInput(filename, error, open_flags, 0); if (wpc) { if (length_in_ms) *length_in_ms = (int)(WavpackGetNumSamples(wpc) * 1000.0 / WavpackGetSampleRate(wpc)); if (title && WavpackGetTagItem(wpc, "title", NULL, 0)) { char art [128], ttl [128]; WavpackGetTagItem(wpc, "title", ttl, sizeof (ttl)); if (WavpackGetMode(wpc) & MODE_APETAG) UTF8ToAnsi(ttl, sizeof (ttl)); if (WavpackGetTagItem(wpc, "artist", art, sizeof (art))) { if (WavpackGetMode(wpc) & MODE_APETAG) UTF8ToAnsi(art, sizeof (art)); StringCchPrintf(title, GETFILEINFO_TITLE_LENGTH, "%s - %s", art, ttl); } else lstrcpyn(title, ttl, GETFILEINFO_TITLE_LENGTH); } wpc = WavpackCloseFile(wpc); } if (title && !*title) { char *p = (char*)filename + strlen (filename); while (*p != '\\' && p >= filename) p--; lstrcpyn(title, ++p, GETFILEINFO_TITLE_LENGTH); } } } void eq_set(int on, char data [10], int preamp) { // most plug-ins can't even do an EQ anyhow.. I'm working on writing // a generic PCM EQ, but it looks like it'll be a little too CPU // consuming to be useful :) } static int read_samples(struct wpcnxt *cnxt, int num_samples); DWORD WINAPI __stdcall DecodeThread(void *b) { int num_chans, sample_rate; int done = 0; memset(curr.error, 0, sizeof (curr.error)); num_chans = WavpackGetReducedChannels(curr.wpc); sample_rate = WavpackGetSampleRate(curr.wpc); while (!*((int *)b) ) { if (seek_needed != -1) { int seek_position = seek_needed; int bc = 0; seek_needed = -1; if (seek_position > getlength() - 1000 && getlength() > 1000) seek_position = getlength() - 1000; // don't seek to last second mod.outMod->Flush(decode_pos_ms = seek_position); if (WavpackSeekSample(curr.wpc, (int)(sample_rate / 1000.0 * seek_position))) { decode_pos_ms = (int)(WavpackGetSampleIndex(curr.wpc) * 1000.0 / sample_rate); mod.outMod->Flush(decode_pos_ms); continue; } else done = 1; } 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 * num_chans * (curr.output_bits / 8)) << (mod.dsp_isactive () ? 1 : 0))) { int tsamples = read_samples (&curr, 576) * num_chans; int tbytes = tsamples * (curr.output_bits/8); if (tsamples) { mod.SAAddPCMData((char *) curr.sample_buffer, num_chans, curr.output_bits, decode_pos_ms); mod.VSAAddPCMData((char *) curr.sample_buffer, num_chans, curr.output_bits, decode_pos_ms); decode_pos_ms = (int)(WavpackGetSampleIndex(curr.wpc) * 1000.0 / sample_rate); if (mod.dsp_isactive()) tbytes = mod.dsp_dosamples ((short *) curr.sample_buffer, tsamples / num_chans, curr.output_bits, num_chans, sample_rate) * (num_chans * (curr.output_bits/8)); mod.outMod->Write ((char *) curr.sample_buffer, tbytes); } else done = 1; } else { mod.SetInfo((int) ((WavpackGetInstantBitrate (curr.wpc) + 500.0) / 1000.0), -1, -1, 1); Sleep(20); } } return 0; } /********* These functions provide the "transcoding" mode of winamp. *********/ extern "C" __declspec (dllexport) intptr_t winampGetExtendedRead_open (const char *fn, int *size, int *bps, int *nch, int *srate) { struct wpcnxt *cnxt = (struct wpcnxt *)malloc(sizeof (struct wpcnxt)); int num_chans, sample_rate, open_flags; char error[128]; #ifdef DEBUG_CONSOLE sprintf (error, "Read_open (%s)\n", fn); debug_write (error); #endif if (!cnxt) return 0; memset(cnxt, 0, sizeof (struct wpcnxt)); open_flags = OPEN_NORMALIZE | OPEN_WVC; if (!(config_bits & ALLOW_MULTICHANNEL) || *nch == 2) open_flags |= OPEN_2CH_MAX; if (config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM)) open_flags |= OPEN_TAGS; cnxt->wpc = WavpackOpenFileInput(fn, error, open_flags, 0); if (!cnxt->wpc) // error opening file, just return error { free (cnxt); return 0; } num_chans = WavpackGetReducedChannels(cnxt->wpc); sample_rate = WavpackGetSampleRate(cnxt->wpc); if (num_chans > MAX_NCH) { WavpackCloseFile(cnxt->wpc); free (cnxt); return 0; } if (*bps != 16 && *bps != 24 && *bps != 32) { cnxt->output_bits = WavpackGetBitsPerSample(cnxt->wpc) > 16 ? 24 : 16; if (config_bits & ALWAYS_16BIT) cnxt->output_bits = 16; else if ((config_bits & (REPLAYGAIN_TRACK | REPLAYGAIN_ALBUM)) && (config_bits & REPLAYGAIN_24BIT)) cnxt->output_bits = 24; } else cnxt->output_bits = *bps; if (num_chans > MAX_NCH) // don't allow too many channels! { WavpackCloseFile(cnxt->wpc); free (cnxt); return 0; } *nch = num_chans; *srate = sample_rate; *bps = cnxt->output_bits; *size = WavpackGetNumSamples(cnxt->wpc) * (*bps / 8) * (*nch); cnxt->play_gain = calculate_gain(cnxt->wpc); #ifdef DEBUG_CONSOLE sprintf (error, "Read_open success! nch=%d, srate=%d, bps=%d, size=%d\n", *nch, *srate, *bps, *size); debug_write (error); #endif return (intptr_t) cnxt; } extern "C" __declspec (dllexport) intptr_t winampGetExtendedRead_getData (intptr_t handle, char *dest, int len, int *killswitch) { struct wpcnxt *cnxt = (struct wpcnxt *)handle; int num_chans = WavpackGetReducedChannels(cnxt->wpc); int bytes_per_sample = num_chans * cnxt->output_bits / 8; int used = 0; #ifdef DEBUG_CONSOLE char error [128]; #endif while (used < len && !*killswitch) { int nsamples = (len - used) / bytes_per_sample, tsamples; if (!nsamples) break; else if (nsamples > 576) nsamples = 576; tsamples = read_samples(cnxt, nsamples) * num_chans; if (tsamples) { int tbytes = tsamples * (cnxt->output_bits/8); memcpy (dest + used, cnxt->sample_buffer, tbytes); used += tbytes; } else break; } #ifdef DEBUG_CONSOLE sprintf (error, "Read_getData (%d), actualy read %d\n", len, used); debug_write (error); #endif return used; } extern "C" __declspec (dllexport) int winampGetExtendedRead_setTime (intptr_t handle, int millisecs) { struct wpcnxt *cnxt = (struct wpcnxt *) handle; int sample_rate = WavpackGetSampleRate(cnxt->wpc); return WavpackSeekSample(cnxt->wpc, (int)(sample_rate / 1000.0 * millisecs)); } extern "C" __declspec (dllexport) void winampGetExtendedRead_close (intptr_t handle) { struct wpcnxt *cnxt = (struct wpcnxt *) handle; #ifdef DEBUG_CONSOLE char error [128]; sprintf (error, "Read_close ()\n"); debug_write (error); #endif WavpackCloseFile(cnxt->wpc); free (cnxt); } /* This is a generic function to read WavPack samples and convert them to a * form usable by winamp. It includes conversion of any WavPack format * (including ieee float) to 16, 24, or 32-bit integers (with noise shaping * for the 16-bit case) and replay gain implementation (with optional soft * clipping). It is used by both the regular "play" code and the newer * transcoding functions. * * The num_samples parameter is the number of "composite" samples to * convert and is limited currently to 576 samples for legacy reasons. The * return value is the number of samples actually converted and will be * equal to the number requested unless an error occurs or the end-of-file * is encountered. The converted samples are stored (interleaved) at * cnxt->sample_buffer[]. */ static int read_samples (struct wpcnxt *cnxt, int num_samples) { int num_chans = WavpackGetReducedChannels(cnxt->wpc), samples, tsamples; samples = WavpackUnpackSamples(cnxt->wpc, (int32_t*) cnxt->sample_buffer, num_samples); tsamples = samples * num_chans; if (tsamples) { if (!(WavpackGetMode(cnxt->wpc) & MODE_FLOAT)) { float scaler = (float) (1.0 / ((unsigned long) 1 << (WavpackGetBytesPerSample(cnxt->wpc) * 8 - 1))); float *fptr = (float *) cnxt->sample_buffer; long *lptr = cnxt->sample_buffer; int cnt = tsamples; while (cnt--) *fptr++ = *lptr++ * scaler; } if (cnxt->play_gain != 1.0) { float *fptr = (float *) cnxt->sample_buffer; int cnt = tsamples; double outval; while (cnt--) { outval = *fptr * cnxt->play_gain; /*if (cnxt->soft_clipping) { if (outval > 0.75) outval = 1.0 - (0.0625 / (outval - 0.5)); else if (outval < -0.75) outval = -1.0 - (0.0625 / (outval + 0.5)); }*/ *fptr++ = (float) outval; } } if (cnxt->output_bits == 16) { float *fptr = (float *) cnxt->sample_buffer; short *sptr = (short *) cnxt->sample_buffer; int cnt = samples, ch; while (cnt--) for (ch = 0; ch < num_chans; ++ch) { int dst; *fptr -= cnxt->error [ch]; if (*fptr >= 1.0) dst = 32767; else if (*fptr <= -1.0) dst = -32768; else dst = (int) floor (*fptr * 32768.0); cnxt->error [ch] = (float)(dst / 32768.0 - *fptr++); *sptr++ = dst; } } else if (cnxt->output_bits == 24) { unsigned char *cptr = (unsigned char *) cnxt->sample_buffer; float *fptr = (float *) cnxt->sample_buffer; int cnt = tsamples; long outval; while (cnt--) { if (*fptr >= 1.0) outval = 8388607; else if (*fptr <= -1.0) outval = -8388608; else outval = (int) floor (*fptr * 8388608.0); *cptr++ = (unsigned char) outval; *cptr++ = (unsigned char) (outval >> 8); *cptr++ = (unsigned char) (outval >> 16); fptr++; } } else if (cnxt->output_bits == 32) { float *fptr = (float *) cnxt->sample_buffer; long *sptr = (long *) cnxt->sample_buffer; int cnt = tsamples; while (cnt--) { if (*fptr >= 1.0) *sptr++ = 8388607 << 8; else if (*fptr <= -1.0) *sptr++ = -8388608 << 8; else *sptr++ = ((int) floor (*fptr * 8388608.0)) << 8; fptr++; } } } return samples; } extern "C" __declspec (dllexport) In_Module * winampGetInModule2() { return &mod; } // This code provides an interface between the reader callback mechanism that // WavPack uses internally and the standard fstream C library. static int32_t read_bytes(void *id, void *data, int32_t bcount) { FILE *file = id ? *(FILE**)id : NULL; if (file) return (int32_t) fread(data, 1, bcount, file); else return 0; } static uint32_t get_pos(void *id) { FILE *file = id ? *(FILE**)id : NULL; if (file) return ftell(file); else return -1; } static int set_pos_abs(void *id, uint32_t pos) { FILE *file = id ? *(FILE**)id : NULL; if (file) return fseek(file, pos, SEEK_SET); else return 0; } static int set_pos_rel(void *id, int32_t delta, int mode) { FILE *file = id ? *(FILE**)id : NULL; if (file) return fseek(file, delta, mode); else return -1; } static int push_back_byte(void *id, int c) { FILE *file = id ? *(FILE**)id : NULL; if (file) return ungetc(c, file); else return EOF; } static uint32_t get_length(void *id) { FILE *file = id ? *(FILE**)id : NULL; struct stat statbuf; if (!file || fstat (fileno (file), &statbuf) || !(statbuf.st_mode & S_IFREG)) return 0; else return statbuf.st_size; } static int can_seek(void *id) { FILE *file = id ? *(FILE**)id : NULL; struct stat statbuf; return file && !fstat (fileno (file), &statbuf) && (statbuf.st_mode & S_IFREG); } static int32_t write_bytes(void *id, void *data, int32_t bcount) { FILE *file = id ? *(FILE**)id : NULL; if (file) return (int32_t) fwrite (data, 1, bcount, file); else return 0; } static WavpackStreamReader freader = { read_bytes, get_pos, set_pos_abs, set_pos_rel, push_back_byte, get_length, can_seek, write_bytes }; /* These functions provide UNICODE support for the winamp media library */ static int metadata_we_can_write(const char *metadata); static void close_context(struct wpcnxt *cxt) { if (cxt->wpc) WavpackCloseFile(cxt->wpc); if (cxt->wv_id) fclose(cxt->wv_id); if (cxt->wvc_id) fclose(cxt->wvc_id); memset(cxt, 0, sizeof (*cxt)); } #ifdef ANSI_METADATA extern "C" __declspec (dllexport) int winampGetExtendedFileInfo(char *filename, char *metadata, char *ret, int retlen) { int open_flags = OPEN_TAGS; char error[128]; int retval = 0; #ifdef DEBUG_CONSOLE sprintf (error, "winampGetExtendedFileInfo (%s)\n", metadata); debug_write (error); #endif if (!_stricmp(metadata, "type")) { ret[0] = '0'; ret[1] = 0; return 1; } else if (!_stricmp(metadata, "family")) { int len; const char *p; if (!filename || !filename[0]) return 0; len = lstrlen(filename); if (len < 3 || '.' != filename[len - 3]) return 0; p = &filename[len - 2]; if (!_stricmp(p, "wv")) { WASABI_API_LNGSTRING_BUF(IDS_FAMILY_STRING, ret, retlen); return 1; } return 0; } if (!filename || !*filename) return retval; if (!_stricmp(metadata, "length")) { /* even if no file, return a 1 and write "0" */ StringCchPrintf(ret, retlen, "%d", 0); retval = 1; } if (!info.wpc || strcmp(filename, info.lastfn) || !_stricmp(metadata, "formatinformation")) { close_context(&info); if (!(info.wv_id = fopen(filename, "rb"))) return retval; if (config_bits & ALLOW_WVC) { int length = strlen(filename) + 10; char *wvc_name = (char *)malloc(length); if (wvc_name) { lstrcpyn(wvc_name, filename, length); StringCchCat(wvc_name, length, "c"); info.wvc_id = fopen(wvc_name, "rb"); free(wvc_name); } } info.wpc = WavpackOpenFileInputEx(&freader, &info.wv_id, info.wvc_id ? &info.wvc_id : NULL, error, open_flags, 0); if (!info.wpc) { close_context(&info); return retval; } lstrcpyn(info.lastfn, filename, MAX_PATH); info.w_lastfn [0] = 0; } if (!_stricmp(metadata, "formatinformation")) { wchar_t *temp = (wchar_t *)malloc(retlen * sizeof(wchar_t)); generate_format_string(info.wpc, temp, retlen, 0); lstrcpyn(ret, AutoChar(temp), retlen); free(temp); retval = 1; } else if (!_stricmp(metadata, "length")) { StringCchPrintf(ret, retlen, "%d", (int)(WavpackGetNumSamples(info.wpc) * 1000.0 / WavpackGetSampleRate(info.wpc))); retval = 1; } else if (!_stricmp(metadata, "lossless")) { StringCchPrintf (ret, retlen, "%d", (WavpackGetMode(info.wpc) & MODE_LOSSLESS) ? 1 : 0); retval = 1; } else if (!_stricmp(metadata, "numsamples")) { StringCchPrintf(ret, retlen, "%d", WavpackGetNumSamples(info.wpc)); retval = 1; } else if (!_stricmp(metadata, "mime")) { lstrcpyn(ret, L"audio/x-wavpack", retlen); retval = 1; } else if (!_stricmp(metadata, "gain")) { StringCchPrintf(ret, retlen, "%-+.2f dB", calculate_gain(info.wpc, false)); retval = 1; } else if (WavpackGetTagItem(info.wpc, metadata, ret, retlen)) { if (!_stricmp(metadata, "rating")) { int rating = atoi(ret); // appears to be generally 0-5 or 0-100 if (rating > 10) { rating /= 20; } // or maybe we're dealing with a 1-10 range else if (rating > 5) { rating /= 2; } // otherwise it is hopefully in the 0-5 range StringCchPrintf(ret, retlen, "%u", rating); } else { if (WavpackGetMode(info.wpc) & MODE_APETAG) { UTF8ToAnsi(ret, retlen); } } retval = 1; } else if (metadata_we_can_write(metadata)) { if (retlen) *ret = 0; retval = 1; } // This is a little ugly, but since the WavPack library has read the tags off the // files, we can close the files (but not the WavPack context) now so that we don't // leave handles open. We may access the file again for the "formatinformation" // field, so we reopen the file if we get that one. if (info.wv_id) { fclose(info.wv_id); info.wv_id = NULL; } if (info.wvc_id) { fclose(info.wvc_id); info.wvc_id = NULL; } return retval; } #endif #ifdef UNICODE_METADATA extern "C" __declspec (dllexport) int winampGetExtendedFileInfoW (wchar_t *filename, char *metadata, wchar_t *ret, int retlen) { char error[128], res[256]; int open_flags = OPEN_TAGS; int retval = 0; #ifdef DEBUG_CONSOLE sprintf (error, "winampGetExtendedFileInfoW (%s)\n", metadata); debug_write (error); #endif if (!_stricmp(metadata, "type")) { ret[0] = '0'; ret[1] = 0; return 1; } else if (!_stricmp(metadata, "family")) { int len; const wchar_t *p; if (!filename || !filename[0]) return 0; len = lstrlenW(filename); if (len < 3 || L'.' != filename[len - 3]) return 0; p = &filename[len - 2]; if (!_wcsicmp(p, L"wv")) { WASABI_API_LNGSTRINGW_BUF(IDS_FAMILY_STRING, ret, retlen); return 1; } return 0; } if (!filename || !*filename) return retval; if (!_stricmp(metadata, "length")) /* even if no file, return a 1 and write "0" */ { StringCchPrintfW(ret, retlen, L"%d", 0); retval = 1; } if (!info.wpc || wcscmp(filename, info.w_lastfn) || !_stricmp(metadata, "formatinformation")) { close_context(&info); if (!(info.wv_id = _wfopen(filename, L"rb"))) return retval; if (config_bits & ALLOW_WVC) { int length = wcslen(filename) + 10; wchar_t *wvc_name = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wvc_name) { lstrcpynW(wvc_name, filename, length); StringCchCatW(wvc_name, length, L"c"); info.wvc_id = _wfopen(wvc_name, L"rb"); free(wvc_name); } } info.wpc = WavpackOpenFileInputEx(&freader, &info.wv_id, info.wvc_id ? &info.wvc_id : NULL, error, open_flags, 0); if (!info.wpc) { close_context(&info); return retval; } lstrcpynW(info.w_lastfn, filename, MAX_PATH); info.lastfn[0] = 0; } if (!_stricmp(metadata, "formatinformation")) { generate_format_string(info.wpc, ret, retlen, 0); retval = 1; } else if (!_stricmp (metadata, "length")) { StringCchPrintfW(ret, retlen, L"%d", (int)(WavpackGetNumSamples(info.wpc) * 1000.0 / WavpackGetSampleRate(info.wpc))); retval = 1; } else if (!_stricmp(metadata, "lossless")) { StringCchPrintfW(ret, retlen, L"%d", (WavpackGetMode(info.wpc) & MODE_LOSSLESS) ? 1 : 0); retval = 1; } else if (!_stricmp(metadata, "gain")) { StringCchPrintfW(ret, retlen, L"%-+.2f dB", calculate_gain(info.wpc, false)); retval = 1; } else if (!_stricmp(metadata, "numsamples")) { StringCchPrintfW(ret, retlen, L"%d", WavpackGetNumSamples(info.wpc)); retval = 1; } else if (!_stricmp(metadata, "mime")) { lstrcpynW(ret, L"audio/x-wavpack", retlen); retval = 1; } else if (WavpackGetTagItem(info.wpc, metadata, res, sizeof (res))) { if (!_stricmp(metadata, "rating")) { int rating = atoi(res); // appears to be generally 0-5 or 0-100 if (rating > 10) { rating /= 20; } // or maybe we're dealing with a 1-10 range else if (rating > 5) { rating /= 2; } // otherwise it is hopefully in the 0-5 range StringCchPrintfW(ret, retlen, L"%u", rating); } else { if (!(WavpackGetMode(info.wpc) & MODE_APETAG)) lstrcpynW(ret, AutoWide(res), retlen); else lstrcpynW(ret, AutoWide(res, CP_UTF8), retlen); } retval = 1; } else if (metadata_we_can_write (metadata)) { if (retlen) *ret = 0; retval = 1; } // This is a little ugly, but since the WavPack library has read the tags off the // files, we can close the files (but not the WavPack context) now so that we don't // leave handles open. We may access the file again for the "formatinformation" // field, so we reopen the file if we get that one. if (info.wv_id) { fclose (info.wv_id); info.wv_id = NULL; } if (info.wvc_id) { fclose (info.wvc_id); info.wvc_id = NULL; } return retval; } #endif #ifdef ANSI_METADATA extern "C" int __declspec (dllexport) winampSetExtendedFileInfo(const char *filename, const char *metadata, char *val) { char error[128]; #ifdef DEBUG_CONSOLE sprintf (error, "winampSetExtendedFileInfo (%s=%s)\n", metadata, val); debug_write (error); #endif if (!filename || !*filename || !metadata_we_can_write(metadata)) return 0; if (!edit.wpc || strcmp(filename, edit.lastfn)) { if (edit.wpc) WavpackCloseFile(edit.wpc); edit.wpc = WavpackOpenFileInput(filename, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0); if (!edit.wpc) return 0; lstrcpyn(edit.lastfn, filename, MAX_PATH); edit.w_lastfn [0] = 0; } if (strlen(val)) return WavpackAppendTagItem(edit.wpc, metadata, val, strlen (val)); else return WavpackDeleteTagItem(edit.wpc, metadata); } #endif #ifdef UNICODE_METADATA extern "C" int __declspec (dllexport) winampSetExtendedFileInfoW(const wchar_t *filename, const char *metadata, wchar_t *val) { char error[128], utf8_val[256]; lstrcpyn(utf8_val,AutoChar(val, CP_UTF8),sizeof(utf8_val) - 1); #ifdef DEBUG_CONSOLE sprintf (error, "winampSetExtendedFileInfoW (%s=%s)\n", metadata, utf8_val); debug_write (error); #endif if (!filename || !*filename || !metadata_we_can_write(metadata)) return 0; if (!edit.wpc || wcscmp(filename, edit.w_lastfn)) { if (edit.wpc) { WavpackCloseFile(edit.wpc); edit.wpc = NULL; } if (edit.wv_id) fclose(edit.wv_id); if (!(edit.wv_id = _wfopen(filename, L"r+b"))) return 0; edit.wpc = WavpackOpenFileInputEx(&freader, &edit.wv_id, NULL, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0); if (!edit.wpc) { fclose(edit.wv_id); return 0; } lstrcpynW(edit.w_lastfn, filename, MAX_PATH); edit.lastfn [0] = 0; } if (strlen(utf8_val)) return WavpackAppendTagItem(edit.wpc, metadata, utf8_val, strlen (utf8_val)); else return WavpackDeleteTagItem(edit.wpc, metadata); } #endif extern "C" int __declspec (dllexport) winampWriteExtendedFileInfo(void) { #ifdef DEBUG_CONSOLE debug_write ("winampWriteExtendedFileInfo ()\n"); #endif if (edit.wpc) { WavpackWriteTag(edit.wpc); WavpackCloseFile(edit.wpc); edit.wpc = NULL; } if (edit.wv_id) { fclose(edit.wv_id); edit.wv_id = NULL; } close_context(&info); // make sure we re-read info on any open files return 1; } // return 1 if you want winamp to show it's own file info dialogue, 0 if you want to show your own (via In_Module.InfoBox) // if returning 1, remember to implement winampGetExtendedFileInfo("formatinformation")! extern "C" __declspec(dllexport) int winampUseUnifiedFileInfoDlg(const wchar_t * fn) { return 1; } static const char *writable_metadata [] = { "track", "genre", "year", "comment", "artist", "album", "title", "albumartist", "composer", "publisher", "disc", "tool", "encoder", "bpm", "category", "rating", "replaygain_track_gain", "replaygain_track_peak", "replaygain_album_gain", "replaygain_album_peak" }; #define NUM_KNOWN_METADATA (sizeof (writable_metadata) / sizeof (writable_metadata [0])) static int metadata_we_can_write(const char *metadata) { if (!metadata || !*metadata) return 0; for (int i = 0; i < NUM_KNOWN_METADATA; ++i) if (!_stricmp(metadata, writable_metadata[i])) return 1; return 0; } static void generate_format_string(WavpackContext *wpc, wchar_t *string, int maxlen, int wide) { int mode = WavpackGetMode(wpc); unsigned char md5_sum[16]; wchar_t modes[256]; wchar_t fmt[256]; WASABI_API_LNGSTRINGW_BUF(IDS_ENCODER_VERSION, fmt, sizeof(fmt)); StringCchPrintfW(string, maxlen, fmt, WavpackGetVersion(wpc)); while (*string && string++ && maxlen--); WASABI_API_LNGSTRINGW_BUF(IDS_SOURCE, fmt, sizeof (fmt)); StringCchPrintfW(string, maxlen, fmt, WavpackGetBitsPerSample(wpc), WASABI_API_LNGSTRINGW((WavpackGetMode(wpc) & MODE_FLOAT) ? IDS_FLOATS : IDS_INTS), WavpackGetSampleRate(wpc)); while (*string && string++ && maxlen--); if (WavpackGetNumChannels(wpc) > 2) { WASABI_API_LNGSTRINGW_BUF(IDS_MULTICHANNEL, fmt, sizeof (fmt)); StringCchPrintfW(string, maxlen, fmt, WavpackGetNumChannels(wpc)); while (*string && string++ && maxlen--); } else if (WavpackGetNumChannels(wpc) == 2) { WASABI_API_LNGSTRINGW_BUF(IDS_STEREO, fmt, sizeof (fmt)); StringCchPrintfW(string, maxlen, fmt); while (*string && string++ && maxlen--); } else { WASABI_API_LNGSTRINGW_BUF(IDS_MONO, fmt, sizeof (fmt)); StringCchPrintfW(string, maxlen, fmt); while (*string && string++ && maxlen--); } modes [0] = 0; if (WavpackGetMode(wpc) & MODE_HYBRID) { StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_HYBRID)); StringCchCatW(modes, 256, L" "); } StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW((WavpackGetMode(wpc) & MODE_LOSSLESS) ? IDS_LOSSLESS : IDS_LOSSY)); if (WavpackGetMode(wpc) & MODE_FAST) StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_FAST)); else if (WavpackGetMode(wpc) & MODE_VERY_HIGH) StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_VHIGH)); else if (WavpackGetMode(wpc) & MODE_HIGH) StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_HIGH)); if (WavpackGetMode(wpc) & MODE_EXTRA) StringCchCatW(modes, 256, WASABI_API_LNGSTRINGW(IDS_EXTRA)); StringCchPrintfW(string, maxlen, L"%s:%s %s\n", WASABI_API_LNGSTRINGW(IDS_MODES), (wide || lstrlenW(modes) < 24) ? L"" : L"\n", modes); while (*string && string++ && maxlen--); if (WavpackGetRatio(wpc) != 0.0) { wchar_t str_kbps[32]; StringCchPrintfW(string, maxlen, L"%s: %d %s \n", WASABI_API_LNGSTRINGW(IDS_BITRATE), (int)((WavpackGetAverageBitrate(wpc, TRUE) + 500.0) / 1000.0), WASABI_API_LNGSTRINGW_BUF(IDS_KBPS, str_kbps, sizeof(str_kbps))); while (*string && string++ && maxlen--); StringCchPrintfW(string, maxlen, L"%s: %.2f : 1 \n", WASABI_API_LNGSTRINGW(IDS_RATIO), 1.0 / WavpackGetRatio(wpc)); while (*string && string++ && maxlen--); } if (WavpackGetMD5Sum(wpc, md5_sum)) { wchar_t md5s1 [17], md5s2 [17]; int i; for (i = 0; i < 8; ++i) { StringCchPrintfW(md5s1 + i * 2, sizeof(md5s1), L"%02x", md5_sum [i]); StringCchPrintfW(md5s2 + i * 2, sizeof(md5s2), L"%02x", md5_sum [i+8]); } StringCchPrintfW(string, maxlen, (wide ? L"%s: %s%s\n" : L"%s:\n %s\n %s\n"), WASABI_API_LNGSTRINGW(IDS_MD5), md5s1, md5s2); } } ///////////////////// native "C" functions required for AlbumArt support /////////////////////////// #ifdef DEBUG_CONSOLE static char temp_buff [256]; static char *wide2char (const wchar_t *src) { char *dst = temp_buff; while (*src) *dst++ = (char) *src++; *dst = 0; return temp_buff; } #endif int WavPack_HandlesExtension(const wchar_t *extension) { return !_wcsicmp(extension, L"wv"); } int WavPack_GetAlbumArt(const wchar_t *filename, const wchar_t *type, void **bits, size_t *len, wchar_t **mime_type) { char *buffer; char error[128]; int tag_size, i; int retval = 1; #ifdef DEBUG_CONSOLE sprintf (error, "WavPack_GetAlbumArt (%s)\n", wide2char (type)); debug_write (error); #endif if (!filename || !*filename || _wcsicmp(type, L"cover")) return retval; if (!info.wpc || wcscmp(filename, info.w_lastfn)) { close_context(&info); if (!(info.wv_id = _wfopen(filename, L"rb"))) return retval; if (config_bits & ALLOW_WVC) { int length = wcslen(filename) + 10; wchar_t *wvc_name = (wchar_t *)malloc(length * sizeof(wchar_t)); if (wvc_name) { lstrcpynW(wvc_name, filename, length); StringCchCatW(wvc_name, length, L"c"); info.wvc_id = _wfopen(wvc_name, L"rb"); free(wvc_name); } } info.wpc = WavpackOpenFileInputEx(&freader, &info.wv_id, info.wvc_id ? &info.wvc_id : NULL, error, OPEN_TAGS, 0); if (!info.wpc) { close_context(&info); return retval; } lstrcpynW(info.w_lastfn, filename, MAX_PATH); info.lastfn[0] = 0; } tag_size = WavpackGetBinaryTagItem(info.wpc, "Cover Art (Front)", NULL, 0); if (!tag_size) return retval; buffer = (char*)WASABI_API_MEMMGR->sysMalloc(tag_size); WavpackGetBinaryTagItem(info.wpc, "Cover Art (Front)", buffer, tag_size); for (i = 0; i < tag_size - 1; ++i) if (!buffer[i] && strrchr(buffer, '.')) { char *ext = strrchr(buffer, '.') + 1; wchar_t *wcptr; wcptr = *mime_type = (wchar_t*)WASABI_API_MEMMGR->sysMalloc(strlen(ext) * 2 + 2); while (*ext) *wcptr++ = *ext++; *wcptr = 0; *bits = buffer; *len = tag_size - i - 1; memmove(buffer, buffer + i + 1, *len); retval = 0; #ifdef DEBUG_CONSOLE sprintf (error, "WavPack_GetAlbumArt (\"%s\", %d) success!\n", wide2char (*mime_type), *len); debug_write (error); #endif } if (retval) WASABI_API_MEMMGR->sysFree(buffer); // This is a little ugly, but since the WavPack library has read the tags off the // files, we can close the files (but not the WavPack context) now so that we don't // leave handles open. We may access the file again for the "formatinformation" // field, so we reopen the file if we get that one. if (info.wv_id) { fclose(info.wv_id); info.wv_id = NULL; } if (info.wvc_id) { fclose(info.wvc_id); info.wvc_id = NULL; } return retval; } int WavPack_SetAlbumArt(const wchar_t *filename, const wchar_t *type, void *bits, size_t len, const wchar_t *mime_type) { #if 1 return 2; // return 2 to indicate "read-only" cover art for now #else char error [128], name [50], *cp; int tag_size, retval = 0; unsigned char *buffer; #ifdef DEBUG_CONSOLE sprintf (error, "WavPack_SetAlbumArt (%s)\n", wide2char (mime_type)); debug_write (error); #endif if (!filename || !*filename || _wcsicmp (type, L"cover") || wcslen (mime_type) > 16) return 1; strcpy (name, "Cover Art (Front)"); cp = name + strlen (name); *cp++ = '.'; while (*mime_type) *cp++ = (char) *mime_type++; *cp = 0; tag_size = strlen (name) + 1 + len; buffer = malloc (tag_size); strcpy (buffer, name); memcpy (buffer + strlen (buffer) + 1, bits, len); if (!edit.wpc || wcscmp (filename, edit.w_lastfn)) { if (edit.wpc) { WavpackCloseFile (edit.wpc); edit.wpc = NULL; } if (edit.wv_id) fclose (edit.wv_id); if (!(edit.wv_id = _wfopen (filename, L"r+b"))) { free (buffer); return 1; } edit.wpc = WavpackOpenFileInputEx (&freader, &edit.wv_id, NULL, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0); if (!edit.wpc) { fclose (edit.wv_id); free (buffer); return 1; } wcscpy (edit.w_lastfn, filename); edit.lastfn [0] = 0; } retval = WavpackAppendTagItem (edit.wpc, "Cover Art (Front)", buffer, tag_size); free (buffer); if (retval) { winampWriteExtendedFileInfo (); return 0; } else { close_context (&edit); return 1; } #endif } int WavPack_DeleteAlbumArt(const wchar_t *filename, const wchar_t *type) { #if 1 return 2; // return 2 to indicate "read-only" cover art for now #else char error [128]; #ifdef DEBUG_CONSOLE sprintf (error, "WavPack_DeleteAlbumArt ()\n"); debug_write (error); #endif if (!filename || !*filename || _wcsicmp (type, L"cover")) return 0; if (!edit.wpc || wcscmp (filename, edit.w_lastfn)) { if (edit.wpc) { WavpackCloseFile (edit.wpc); edit.wpc = NULL; } if (edit.wv_id) fclose (edit.wv_id); if (!(edit.wv_id = _wfopen (filename, L"r+b"))) return 1; edit.wpc = WavpackOpenFileInputEx (&freader, &edit.wv_id, NULL, error, OPEN_TAGS | OPEN_EDIT_TAGS, 0); if (!edit.wpc) { fclose (edit.wv_id); return 1; } wcscpy (edit.w_lastfn, filename); edit.lastfn [0] = 0; } if (WavpackDeleteTagItem (edit.wpc, "Cover Art (Front)")) { winampWriteExtendedFileInfo (); return 0; } else { close_context (&edit); return 1; } #endif } ////////////////////////////////////////////////////////////////////////////// // This function uses the ReplayGain mode selected by the user and the info // // stored in the specified tag to determine the gain value used to play the // // file and whether "soft clipping" is required. Note that the gain is in // // voltage scaling (not dB), so a value of 1.0 (not 0.0) is unity gain. // ////////////////////////////////////////////////////////////////////////////// static float calculate_gain(WavpackContext *wpc, bool allowDefault) { if (AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain", false)) { float dB = 0, peak = 1.0f; char gain[128] = "", peakVal[128] = ""; _locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale(); switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_source", 0)) { case 0: // track if ((!WavpackGetTagItem(wpc, "replaygain_track_gain", gain, sizeof(gain)) || !gain[0]) && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) WavpackGetTagItem(wpc, "replaygain_album_gain", gain, sizeof(gain)); if ((!WavpackGetTagItem(wpc, "replaygain_track_peak", peakVal, sizeof(peakVal)) || !peakVal[0]) && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) WavpackGetTagItem(wpc, "replaygain_album_peak", peakVal, sizeof(peakVal)); break; case 1: if ((!WavpackGetTagItem(wpc, "replaygain_album_gain", gain, sizeof(gain)) || !gain[0]) && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) WavpackGetTagItem(wpc, "replaygain_track_gain", gain, sizeof(gain)); if ((!WavpackGetTagItem(wpc, "replaygain_album_peak", peakVal, sizeof(peakVal)) || !peakVal[0]) && !AGAVE_API_CONFIG->GetBool(playbackConfigGroupGUID, L"replaygain_preferred_only", false)) WavpackGetTagItem(wpc, "replaygain_track_peak", peakVal, sizeof(peakVal)); break; } if (gain[0]) { if (gain[0] == L'+') dB = (float)_atof_l(&gain[1],C_locale); else dB = (float)_atof_l(gain,C_locale); } else if (allowDefault) { dB = AGAVE_API_CONFIG->GetFloat(playbackConfigGroupGUID, L"non_replaygain", -6.0); return powf(10.0f, dB / 20.0f); } if (peakVal[0]) { peak = (float)_atof_l(peakVal,C_locale); } switch (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"replaygain_mode", 1)) { case 0: // apply gain return powf(10.0f, dB / 20.0f); case 1: // apply gain, but don't clip return min(powf(10.0f, dB / 20.0f), 1.0f / peak); case 2: // normalize return 1.0f / peak; case 3: // prevent clipping if (peak > 1.0f) return 1.0f / peak; else return 1.0f; } } return 1.0f; // no gain } // Convert a Ansi string into its Unicode UTF-8 format equivalent. The // conversion is done in-place so the maximum length of the string buffer must // be specified because the string may become longer or shorter. If the // resulting string will not fit in the specified buffer size then it is // truncated. #ifdef OLD_INFO_DIALOG static void AnsiToUTF8(char *string, int len) { int max_chars = (int) strlen(string); wchar_t *temp = (wchar_t *) malloc((max_chars + 1) * 2); MultiByteToWideChar(CP_ACP, 0, string, -1, temp, max_chars + 1); lstrcpyn(string, AutoChar(temp, CP_UTF8), len); free(temp); } #endif // Convert Unicode UTF-8 string to wide format. UTF-8 string must be NULL // terminated. Resulting wide string must be able to fit in provided space // and will also be NULL terminated. The number of characters converted will // be returned (not counting terminator). static int UTF8ToWideChar(const char *pUTF8, wchar_t *pWide) { int trail_bytes = 0; int chrcnt = 0; while (*pUTF8) { if (*pUTF8 & 0x80) { if (*pUTF8 & 0x40) { if (trail_bytes) { trail_bytes = 0; chrcnt++; } else { char temp = *pUTF8; while (temp & 0x80) { trail_bytes++; temp <<= 1; } pWide [chrcnt] = temp >> trail_bytes--; } } else if (trail_bytes) { pWide [chrcnt] = (pWide [chrcnt] << 6) | (*pUTF8 & 0x3f); if (!--trail_bytes) chrcnt++; } } else pWide [chrcnt++] = *pUTF8; pUTF8++; } pWide [chrcnt] = 0; return chrcnt; } // Convert a Unicode UTF-8 format string into its Ansi equivalent. The // conversion is done in-place so the maximum length of the string buffer must // be specified because the string may become longer or shorter. If the // resulting string will not fit in the specified buffer size then it is // truncated. static void UTF8ToAnsi(char *string, int len) { int max_chars = (int)strlen(string); wchar_t *temp = (wchar_t *)malloc((max_chars + 1) * 2); int act_chars = UTF8ToWideChar(string, temp); while (act_chars) { memset (string, 0, len); if (WideCharToMultiByte(CP_ACP, 0, temp, act_chars, string, len - 1, NULL, NULL)) break; else act_chars--; } if (!act_chars) *string = 0; free (temp); } extern "C" __declspec(dllexport) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) { // as we're not hooking anything and have no settings we can support an on-the-fly uninstall action return IN_PLUGIN_UNINSTALL_NOW; }