/* * in_asap.c - ASAP plugin for Winamp * * Copyright (C) 2005-2009 Piotr Fusik * * This file is part of ASAP (Another Slight Atari Player), * see http://asap.sourceforge.net * * ASAP is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2 of the License, * or (at your option) any later version. * * ASAP 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ASAP; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "in2.h" #include "ipc_pe.h" #include "wa_ipc.h" #include "asap.h" #include "gui.h" // Winamp's equalizer works only with 16-bit samples #define SUPPORT_EQUALIZER 1 static In_Module mod; // configuration #define INI_SECTION "in_asap" static const char *ini_file; // current file ASAP_State asap; char current_filename[MAX_PATH] = ""; static char current_filename_with_song[MAX_PATH + 3] = ""; int current_song; static byte module[ASAP_MODULE_MAX]; static int module_len; static int duration; #define channels asap.module_info.channels static int playlistLength; static HANDLE thread_handle = NULL; static volatile int thread_run = FALSE; static int paused = 0; static int seek_needed; static void writeIniInt(const char *name, int value) { char str[16]; *appendInt(str, value) = '\0'; WritePrivateProfileString(INI_SECTION, name, str, ini_file); } static void config(HWND hwndParent) { if (settingsDialog(mod.hDllInstance, hwndParent)) { writeIniInt("song_length", song_length); writeIniInt("silence_seconds", silence_seconds); writeIniInt("play_loops", play_loops); writeIniInt("mute_mask", mute_mask); } } static void about(HWND hwndParent) { MessageBox(hwndParent, ASAP_CREDITS "\n" ASAP_COPYRIGHT, "About ASAP Winamp plugin " ASAP_VERSION, MB_OK); } static int extractSongNumber(const char *s, char *filename) { int i = strlen(s); int song = -1; if (i > 6 && s[i - 1] >= '0' && s[i - 1] <= '9') { if (s[i - 2] == '#') { song = s[i - 1] - '1'; i -= 2; } else if (s[i - 2] >= '0' && s[i - 2] <= '9' && s[i - 3] == '#') { song = (s[i - 2] - '0') * 10 + s[i - 1] - '1'; i -= 3; } } memcpy(filename, s, i); filename[i] = '\0'; return song; } static void expandFileSongs(HWND playlistWnd, int index) { const char *fn; fileinfo fi; int song; ASAP_ModuleInfo module_info; char *p; int j; fn = (const char *) SendMessage(mod.hMainWindow, WM_WA_IPC, index, IPC_GETPLAYLISTFILE); song = extractSongNumber(fn, fi.file); if (song >= 0 || !ASAP_IsOurFile(fi.file)) return; if (!loadModule(fi.file, module, &module_len)) return; if (!ASAP_GetModuleInfo(&module_info, fi.file, module, module_len)) return; SendMessage(playlistWnd, WM_WA_IPC, IPC_PE_DELETEINDEX, index); p = fi.file + strlen(fi.file); for (j = 0; j < module_info.songs; j++) { COPYDATASTRUCT cds; *p = '#'; *appendInt(p + 1, j + 1) = '\0'; fi.index = index + j; cds.dwData = IPC_PE_INSERTFILENAME; cds.lpData = &fi; cds.cbData = sizeof(fileinfo); SendMessage(playlistWnd, WM_COPYDATA, 0, (LPARAM) &cds); } } static INT_PTR CALLBACK progressDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_INITDIALOG) SendDlgItemMessage(hDlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, playlistLength)); return FALSE; } static void expandPlaylistSongs(void) { static BOOL processing = FALSE; HWND playlistWnd; HWND progressWnd; int index; if (processing) return; playlistWnd = (HWND) SendMessage(mod.hMainWindow, WM_WA_IPC, IPC_GETWND_PE, IPC_GETWND); if (playlistWnd == NULL) return; processing = TRUE; playlistLength = SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETLISTLENGTH); progressWnd = CreateDialog(mod.hDllInstance, MAKEINTRESOURCE(IDD_PROGRESS), mod.hMainWindow, progressDialogProc); index = playlistLength; while (--index >= 0) { if ((index & 15) == 0) SendDlgItemMessage(progressWnd, IDC_PROGRESS, PBM_SETPOS, playlistLength - index, 0); expandFileSongs(playlistWnd, index); } DestroyWindow(progressWnd); processing = FALSE; } static void init(void) { ini_file = (const char *) SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GETINIFILE); song_length = GetPrivateProfileInt(INI_SECTION, "song_length", song_length, ini_file); silence_seconds = GetPrivateProfileInt(INI_SECTION, "silence_seconds", silence_seconds, ini_file); play_loops = GetPrivateProfileInt(INI_SECTION, "play_loops", play_loops, ini_file); mute_mask = GetPrivateProfileInt(INI_SECTION, "mute_mask", mute_mask, ini_file); } static void quit(void) { } static int title_song; static ASAP_ModuleInfo title_module_info; static void getTitle(char *title) { char *p = appendString(title, title_module_info.name); if (title_module_info.songs > 1) { p = appendString(p, " (song "); p = appendInt(p, title_song + 1); *p++ = ')'; } *p = '\0'; } static char *tagFunc(char *tag, void *p) { if (stricmp(tag, "artist") == 0 && title_module_info.author[0] != '\0') return strdup(title_module_info.author); if (stricmp(tag, "title") == 0) { char *title = malloc(strlen(title_module_info.name) + 11); if (title != NULL) getTitle(title); return title; } return NULL; } static void tagFreeFunc(char *tag, void *p) { free(tag); } static void getFileInfo(char *file, char *title, int *length_in_ms) { char filename[MAX_PATH]; if (file == NULL || file[0] == '\0') file = current_filename_with_song; title_song = extractSongNumber(file, filename); if (title_song < 0) expandPlaylistSongs(); if (!loadModule(filename, module, &module_len)) return; if (!ASAP_GetModuleInfo(&title_module_info, filename, module, module_len)) return; if (title_song < 0) title_song = title_module_info.default_song; if (title != NULL) { waFormatTitle fmt_title = { NULL, NULL, title, 512, tagFunc, tagFreeFunc }; getTitle(title); // in case IPC_FORMAT_TITLE doesn't work... SendMessage(mod.hMainWindow, WM_WA_IPC, (WPARAM) &fmt_title, IPC_FORMAT_TITLE); } if (length_in_ms != NULL) *length_in_ms = getSongDuration(&title_module_info, title_song); } static int infoBox(char *file, HWND hwndParent) { char filename[MAX_PATH]; int song; song = extractSongNumber(file, filename); showInfoDialog(mod.hDllInstance, hwndParent, filename, song); return 0; } static int isOurFile(char *fn) { char filename[MAX_PATH]; extractSongNumber(fn, filename); return ASAP_IsOurFile(filename); } static DWORD WINAPI playThread(LPVOID dummy) { while (thread_run) { static #if BITS_PER_SAMPLE == 8 byte #else short #endif buffer[BUFFERED_BLOCKS * 2 #if SUPPORT_EQUALIZER * 2 #endif ]; int buffered_bytes = BUFFERED_BLOCKS * channels * (BITS_PER_SAMPLE / 8); if (seek_needed >= 0) { mod.outMod->Flush(seek_needed); ASAP_Seek(&asap, seek_needed); seek_needed = -1; } if (mod.outMod->CanWrite() >= buffered_bytes #if SUPPORT_EQUALIZER << mod.dsp_isactive() #endif ) { int t; buffered_bytes = ASAP_Generate(&asap, buffer, buffered_bytes, BITS_PER_SAMPLE); if (buffered_bytes <= 0) { mod.outMod->CanWrite(); if (!mod.outMod->IsPlaying()) { PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0); return 0; } Sleep(10); continue; } t = mod.outMod->GetWrittenTime(); mod.SAAddPCMData(buffer, channels, BITS_PER_SAMPLE, t); mod.VSAAddPCMData(buffer, channels, BITS_PER_SAMPLE, t); #if SUPPORT_EQUALIZER t = buffered_bytes / (channels * (BITS_PER_SAMPLE / 8)); t = mod.dsp_dosamples((short *) buffer, t, BITS_PER_SAMPLE, channels, ASAP_SAMPLE_RATE); t *= channels * (BITS_PER_SAMPLE / 8); mod.outMod->Write((char *) buffer, t); #else mod.outMod->Write((char *) buffer, buffered_bytes); #endif } else Sleep(20); } return 0; } static int play(char *fn) { int song; int maxlatency; DWORD threadId; strcpy(current_filename_with_song, fn); song = extractSongNumber(fn, current_filename); if (!loadModule(current_filename, module, &module_len)) return -1; if (!ASAP_Load(&asap, current_filename, module, module_len)) return 1; if (song < 0) song = asap.module_info.default_song; duration = playSong(song); maxlatency = mod.outMod->Open(ASAP_SAMPLE_RATE, channels, BITS_PER_SAMPLE, -1, -1); if (maxlatency < 0) return 1; mod.SetInfo(BITS_PER_SAMPLE, ASAP_SAMPLE_RATE / 1000, channels, 1); mod.SAVSAInit(maxlatency, ASAP_SAMPLE_RATE); // the order of VSASetInfo's arguments in in2.h is wrong! // http://forums.winamp.com/showthread.php?postid=1841035 mod.VSASetInfo(ASAP_SAMPLE_RATE, channels); mod.outMod->SetVolume(-666); seek_needed = -1; thread_run = TRUE; thread_handle = CreateThread(NULL, 0, playThread, NULL, 0, &threadId); if (playing_info) updateInfoDialog(current_filename, song); return thread_handle != NULL ? 0 : 1; } static void pause(void) { paused = 1; mod.outMod->Pause(1); } static void unPause(void) { paused = 0; mod.outMod->Pause(0); } static int isPaused(void) { return paused; } static void stop(void) { if (thread_handle != NULL) { thread_run = FALSE; // wait max 10 seconds if (WaitForSingleObject(thread_handle, 10 * 1000) == WAIT_TIMEOUT) TerminateThread(thread_handle, 0); CloseHandle(thread_handle); thread_handle = NULL; } mod.outMod->Close(); mod.SAVSADeInit(); } static int getLength(void) { return duration; } static int getOutputTime(void) { return mod.outMod->GetOutputTime(); } static void setOutputTime(int time_in_ms) { seek_needed = time_in_ms; } static void setVolume(int volume) { mod.outMod->SetVolume(volume); } static void setPan(int pan) { mod.outMod->SetPan(pan); } static void eqSet(int on, char data[10], int preamp) { } static In_Module mod = { IN_VER, "ASAP " ASAP_VERSION, 0, 0, // filled by Winamp "SAP\0Slight Atari Player (*.SAP)\0" "CMC;CM3;CMR;CMS;DMC\0Chaos Music Composer (*.CMC;*.CM3;*.CMR;*.CMS;*.DMC)\0" "DLT\0Delta Music Composer (*.DLT)\0" "MPT;MPD\0Music ProTracker (*.MPT;*.MPD)\0" "RMT\0Raster Music Tracker (*.RMT)\0" "TMC;TM8\0Theta Music Composer 1.x (*.TMC;*.TM8)\0" "TM2\0Theta Music Composer 2.x (*.TM2)\0" , 1, // is_seekable 1, // UsesOutputPlug config, about, init, quit, getFileInfo, infoBox, isOurFile, play, pause, unPause, isPaused, stop, getLength, getOutputTime, setOutputTime, setVolume, setPan, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // filled by Winamp eqSet, NULL, // SetInfo NULL // filled by Winamp }; __declspec(dllexport) In_Module *winampGetInModule2(void) { return &mod; }