commit 3580bc6ce5ed635eba90edb9d4a795f9a99421db Author: grimfang4 Date: Tue Jul 21 02:48:46 2009 +0000 Initial import diff --git a/sfxr/ChangeLog b/sfxr/ChangeLog new file mode 100644 index 0000000..843688f --- /dev/null +++ b/sfxr/ChangeLog @@ -0,0 +1,22 @@ +sfxr-sdl-1.0 +------------ +* Initial SDL port of sfxr by mjau/GerryJJ + +sfxr-sdl-1.1 +------------ +* Various small improvements and 1 bugfix by Hans de Goede + : + * Fix a small bug in the audio setup which could cause a quite noticable + delay between pressing a button and hearing the sound + * Add an icon and .desktop file + * Add a make install target, note: hardcoded to /usr but it does understand + the DESTDIR make parameter + +sfxr-sdl-1.2.0 +------------- +* Changed version number convention: .. +* Added checkbox: Drag bars (the old default) + * Clicking sets the value of a slider +* Sound now plays when an attribute is clicked (a more involved change, like looping, would be needed for dragging) +* Sound now plays on waveform change +* Very simple undo (one slider change) with Z \ No newline at end of file diff --git a/sfxr/Ideas b/sfxr/Ideas new file mode 100644 index 0000000..16b5b4a --- /dev/null +++ b/sfxr/Ideas @@ -0,0 +1,9 @@ + +Make the undo into an array or list (very easy the way I have it set up, just plug it into the undo functions) +Make undo use a full state struct, so everything can be restored. +Add redo (push old undos to the redo list, clear redo list when something is changed) +List of open sounds + +What's up with the file save? If I click Save, then Cancel, it creates a messed up file. + +How would I draw the waveform? It'd be nice to see, but probably a hassle to do. \ No newline at end of file diff --git a/sfxr/Makefile b/sfxr/Makefile new file mode 100644 index 0000000..701125f --- /dev/null +++ b/sfxr/Makefile @@ -0,0 +1,21 @@ +CFLAGS=-ggdb +CXXFLAGS=$(CFLAGS) `sdl-config --cflags` `pkg-config gtk+-2.0 --cflags` +LDFLAGS=`sdl-config --libs` `pkg-config gtk+-2.0 --libs` + +sfxr: main.cpp tools.h sdlkit.h + $(CXX) $(CXXFLAGS) $(LDFLAGS) $< -o $@ + +install: sfxr + mkdir -p $(DESTDIR)/usr/bin + mkdir -p $(DESTDIR)/usr/share/sfxr + mkdir -p $(DESTDIR)/usr/share/applications + mkdir -p $(DESTDIR)/usr/share/icons/hicolor/48x48/apps + install -m 755 sfxr $(DESTDIR)/usr/bin + install -m 644 -p *.tga *.bmp $(DESTDIR)/usr/share/sfxr + install -p -m 644 sfxr.png \ + $(DESTDIR)/usr/share/icons/hicolor/48x48/apps + desktop-file-install --vendor "" \ + --dir $(DESTDIR)/usr/share/applications sfxr.desktop + +clean: + rm sfxr diff --git a/sfxr/ddrawkit.h b/sfxr/ddrawkit.h new file mode 100644 index 0000000..5d4ad16 --- /dev/null +++ b/sfxr/ddrawkit.h @@ -0,0 +1,371 @@ + +/* + + Porting notes + ------------- + + Need to provide window/framebuffer setup at program start + + ddkUnlock() is called when the window should be redrawn + + WinMain() at the bottom of this file is the entry point and main loop + which handles messages and calling the app update function ddkCalcFrame() + + ddkscreen32 = pointer to 640x480 DWORD pixel buffer + mouse_* = mouse info, only need x/y/px/py/left/right/leftclick/rightclick + +*/ + + +#define _WIN32_WINDOWS 0xBAD +#include + +extern "C" long _ftol( double ); //defined by VC6 C libs +extern "C" long _ftol2( double dblSource ) { return _ftol( dblSource ); } + +void ddkInit(); // Will be called on startup +bool ddkCalcFrame(); // Will be called every frame, return true to continue running or false to quit +void ddkFree(); // Will be called on shutdown +bool ddkLock(); // Call immediately before drawing (once per frame) +void ddkUnlock(); // Call immediately after drawing (once per frame) +void ddkDrawPixel(int x, int y, int red, int green, int blue); // Draw a pixel +void ddkSetMode(int width, int height, int bpp, int refreshrate, int fullscreen, char *title); +#define DDK_WINDOW 0 // Run in a normal resizable window (stretch to fit) +#define DDK_FULLSCREEN 1 // Change display mode to the one specified, use vsync +#define DDK_STRETCH 2 // Run in a maximized window, looks like fullscreen +int ddkGetBpp(); // Use this to find out the current color depth +DWORD *ddkscreen32; // Use this for 32 bit modes +WORD *ddkscreen16; // Use this for 16 bit modes +int ddkpitch; // Offset in pixels from the start of one horizontal line to the start of the next + +bool ddrawkit_initialised; +bool ddrawkit_released; +bool ddrawkit_fullscreen; +bool ddrawkit_fullwindow; +int ddrawkit_width; +int ddrawkit_height; +int ddrawkit_bpp; +int ddrawkit_refresh; +bool ddrawkit_timeravailable; + +HWND hWndMain; +HINSTANCE hInstanceMain; + +HCURSOR cursor_arrow; +int mouse_x, mouse_y, mouse_px, mouse_py; +bool mouse_left=false, mouse_right=false, mouse_middle=false; +bool mouse_leftclick=false, mouse_rightclick=false, mouse_middleclick=false; +bool mouse_doubleclick=false; +int mouse_wheel=0; + + +// --- DDB implementation + +struct pBITMAPINFO +{ + BITMAPINFOHEADER bmiHeader; + RGBQUAD bmiColors[256]; +} BMInfo; + +HBITMAP hBM; + +HDC hDC_comp; +DWORD* image_bitmap; + +void InitImageBuffer() +{ + HPALETTE PalHan; + HWND ActiveWindow; + HDC hDC; + + image_bitmap=(DWORD*)malloc(ddrawkit_width*ddrawkit_height*sizeof(DWORD)); + + ActiveWindow=hWndMain; + hDC=GetDC(ActiveWindow); + + BMInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); + BMInfo.bmiHeader.biWidth=ddrawkit_width; + BMInfo.bmiHeader.biHeight=-abs(ddrawkit_height); + BMInfo.bmiHeader.biPlanes=1; + BMInfo.bmiHeader.biBitCount=32; + BMInfo.bmiHeader.biCompression=BI_RGB; + BMInfo.bmiHeader.biSizeImage=ddrawkit_width*ddrawkit_height*4; + BMInfo.bmiHeader.biXPelsPerMeter=0; + BMInfo.bmiHeader.biYPelsPerMeter=0; + + hBM=CreateDIBSection(hDC, (BITMAPINFO*)&BMInfo, DIB_RGB_COLORS, (void**)&image_bitmap, 0, 0); + ReleaseDC(ActiveWindow, hDC); + + ddkscreen32=image_bitmap; + ddkpitch=ddrawkit_width; +} + +void DestroyImageBuffer() +{ + free(image_bitmap); +} + +void ddrawkit_BlitWindowDDB() +{ + HDC hDC; + HBITMAP DefaultBitmap; + + hDC=GetDC(hWndMain); + DefaultBitmap=(HBITMAP)SelectObject(hDC_comp, hBM); +// BitBlt(hDC, x1, y1, x2-x1, y2-y1, hDC_comp, x1, y1, SRCCOPY); + BitBlt(hDC, 0, 0, ddrawkit_width, ddrawkit_height, hDC_comp, 0, 0, SRCCOPY); + SelectObject(hDC_comp, DefaultBitmap); + DeleteDC(hDC); +} + +// --- DDB implementation + + +void ddrawkit_SafeDestroy() +{ + if(!ddrawkit_released) + { + ddrawkit_released=true; + ddkFree(); + DestroyWindow(hWndMain); + DestroyImageBuffer(); + } +} + +LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CLOSE: + ddrawkit_SafeDestroy(); + return 0; + + case WM_SETCURSOR: + if(ddrawkit_fullscreen || ddrawkit_fullwindow) + { + SetCursor(NULL); + return 0; + } + else + SetCursor(cursor_arrow); + break; + + case WM_MOUSEMOVE: + int curwidth, curheight; + RECT rect; + GetClientRect(hWndMain, &rect); + curwidth=rect.right-rect.left; + curheight=rect.bottom-rect.top; + mouse_x=(int)((float)LOWORD(lParam)/curwidth*ddrawkit_width); + mouse_y=(int)((float)HIWORD(lParam)/curheight*ddrawkit_height); + return 0; + case WM_MOUSEWHEEL: + if((wParam>>16)&0x7FFF) + { + if((wParam>>16)&0x8000) + mouse_wheel=-1; + else + mouse_wheel=1; + } + return 0; + case WM_LBUTTONDBLCLK: + mouse_doubleclick=true; + return 0; + case WM_LBUTTONDOWN: + mouse_left=true; + mouse_leftclick=true; + return 0; + case WM_LBUTTONUP: + mouse_left=false; + return 0; + case WM_RBUTTONDOWN: + mouse_right=true; + mouse_rightclick=true; + return 0; + case WM_RBUTTONUP: + mouse_right=false; + return 0; + case WM_MBUTTONDOWN: + mouse_middle=true; + mouse_middleclick=true; + return 0; + case WM_MBUTTONUP: + mouse_middle=false; + return 0; + + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +void ddrawkit_RegisterWindowClass() +{ + WNDCLASSEX wcx; + + ZeroMemory(&wcx, sizeof(WNDCLASSEX)); + wcx.cbSize=sizeof(WNDCLASSEX); + wcx.lpfnWndProc=MainWindowProc; + wcx.hInstance=GetModuleHandle(NULL); + wcx.hIcon=LoadIcon(NULL, IDI_APPLICATION); + wcx.hCursor=NULL; +// wcx.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH); + wcx.hbrBackground=NULL; + wcx.lpszMenuName=NULL; + wcx.lpszClassName="DDrawKitClass"; + RegisterClassEx(&wcx); +} + +bool ddkLock() +{ + return true; +} + +void ddkUnlock() +{ + ddrawkit_BlitWindowDDB(); +} + +void ddkDrawPixel(int x, int y, int red, int green, int blue) +{ + if(x<0 || y<0 || x>=ddrawkit_width || y>=ddrawkit_height) + return; + + if(ddrawkit_bpp==32) + { + DWORD color=(red<<16)|(green<<8)|blue; + ddkscreen32[y*ddkpitch+x]=color; + } + if(ddrawkit_bpp==16) + { + WORD color=((red>>3)<<11)|((green>>2)<<5)|(blue>>3); + ddkscreen16[y*ddkpitch+x]=color; + } +} + +void ddkSetMode(int width, int height, int bpp, int refreshrate, int fullscreen, char *title) +{ + if(!ddrawkit_initialised) + { + ddrawkit_width=width; + ddrawkit_height=height; + ddrawkit_bpp=bpp; + ddrawkit_refresh=refreshrate; + switch(fullscreen) + { + case 0: + ddrawkit_fullscreen=false; + ddrawkit_fullwindow=false; + break; + case 1: + ddrawkit_fullscreen=true; + ddrawkit_fullwindow=false; + break; + case 2: + ddrawkit_fullscreen=false; + ddrawkit_fullwindow=true; + break; + } + + ddrawkit_RegisterWindowClass(); + + if(ddrawkit_fullscreen || ddrawkit_fullwindow) + { + // Screen sized window (possibly stretched) + hWndMain=CreateWindowEx(WS_EX_APPWINDOW, + "DDrawKitClass", title, WS_POPUP, + 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), + NULL, NULL, hInstanceMain, NULL); + } + else + { + // Normal window +// hWndMain=CreateWindow("DDrawKitClass", title, WS_OVERLAPPEDWINDOW, + hWndMain=CreateWindow("DDrawKitClass", title, WS_OVERLAPPED|WS_BORDER|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, +// 0, 0, ddrawkit_width+GetSystemMetrics(SM_CXSIZEFRAME)*2, +// ddrawkit_height+GetSystemMetrics(SM_CYSIZEFRAME)*2+GetSystemMetrics(SM_CYCAPTION), + GetSystemMetrics(SM_CXSCREEN)/2-320, GetSystemMetrics(SM_CYSCREEN)/2-300, + ddrawkit_width+GetSystemMetrics(SM_CXEDGE)*2, + ddrawkit_height+GetSystemMetrics(SM_CYEDGE)*2+GetSystemMetrics(SM_CYCAPTION), + NULL, NULL, hInstanceMain, NULL); + } + + // init DDB implementation + hDC_comp=CreateCompatibleDC(NULL); + InitImageBuffer(); + + ShowWindow(hWndMain, SW_SHOW); + + ddrawkit_initialised=true; + ddrawkit_released=false; + + // Clear screen + ddkLock(); + for(int y=0;y +#include +#include +#include +#include + +#ifdef WIN32 +#include "DPInput.h" // WIN32 +#include "pa/portaudio.h" +#include "fileselector.h" // WIN32 +#else +#include "SDL.h" +#endif + +#define rnd(n) (rand()%(n+1)) + +#define PI 3.14159265f + +float frnd(float range) +{ + return (float)rnd(10000)/10000*range; +} + +struct Spriteset +{ + DWORD *data; + int width; + int height; + int pitch; +}; + +Spriteset font; +Spriteset ld48; + +struct Category +{ + char name[32]; +}; + +Category categories[10]; + +int wave_type; + +float p_base_freq; +float p_freq_limit; +float p_freq_ramp; +float p_freq_dramp; +float p_duty; +float p_duty_ramp; + +float p_vib_strength; +float p_vib_speed; +float p_vib_delay; + +float p_env_attack; +float p_env_sustain; +float p_env_decay; +float p_env_punch; + +bool filter_on; +float p_lpf_resonance; +float p_lpf_freq; +float p_lpf_ramp; +float p_hpf_freq; +float p_hpf_ramp; + +float p_pha_offset; +float p_pha_ramp; + +float p_repeat_speed; + +float p_arp_speed; +float p_arp_mod; + +float master_vol=0.05f; + +float sound_vol=0.5f; + + +bool playing_sample=false; +int phase; +double fperiod; +double fmaxperiod; +double fslide; +double fdslide; +int period; +float square_duty; +float square_slide; +int env_stage; +int env_time; +int env_length[3]; +float env_vol; +float fphase; +float fdphase; +int iphase; +float phaser_buffer[1024]; +int ipp; +float noise_buffer[32]; +float fltp; +float fltdp; +float fltw; +float fltw_d; +float fltdmp; +float fltphp; +float flthp; +float flthp_d; +float vib_phase; +float vib_speed; +float vib_amp; +int rep_time; +int rep_limit; +int arp_time; +int arp_limit; +double arp_mod; + +float* vselected=NULL; +int vcurbutton=-1; + +int wav_bits=16; +int wav_freq=44100; + +int file_sampleswritten; +float filesample=0.0f; +int fileacc=0; + +float* lastChanged = NULL; // For undo +float undoValue = 0.0f; + +void SetUndo(float* valueptr, float oldValue) +{ + lastChanged = valueptr; + undoValue = oldValue; +} + +void Undo() +{ + if(lastChanged != NULL) + { + *lastChanged = undoValue; + lastChanged = NULL; + undoValue = 0.0f; + } +} + +void ResetParams() +{ + lastChanged = NULL; + wave_type=0; + + p_base_freq=0.3f; + p_freq_limit=0.0f; + p_freq_ramp=0.0f; + p_freq_dramp=0.0f; + p_duty=0.0f; + p_duty_ramp=0.0f; + + p_vib_strength=0.0f; + p_vib_speed=0.0f; + p_vib_delay=0.0f; + + p_env_attack=0.0f; + p_env_sustain=0.3f; + p_env_decay=0.4f; + p_env_punch=0.0f; + + filter_on=false; + p_lpf_resonance=0.0f; + p_lpf_freq=1.0f; + p_lpf_ramp=0.0f; + p_hpf_freq=0.0f; + p_hpf_ramp=0.0f; + + p_pha_offset=0.0f; + p_pha_ramp=0.0f; + + p_repeat_speed=0.0f; + + p_arp_speed=0.0f; + p_arp_mod=0.0f; +} + +bool LoadSettings(char* filename) +{ + lastChanged = NULL; + FILE* file=fopen(filename, "rb"); + if(!file) + return false; + + int version=0; + fread(&version, 1, sizeof(int), file); + if(version!=100 && version!=101 && version!=102) + return false; + + fread(&wave_type, 1, sizeof(int), file); + + sound_vol=0.5f; + if(version==102) + fread(&sound_vol, 1, sizeof(float), file); + + fread(&p_base_freq, 1, sizeof(float), file); + fread(&p_freq_limit, 1, sizeof(float), file); + fread(&p_freq_ramp, 1, sizeof(float), file); + if(version>=101) + fread(&p_freq_dramp, 1, sizeof(float), file); + fread(&p_duty, 1, sizeof(float), file); + fread(&p_duty_ramp, 1, sizeof(float), file); + + fread(&p_vib_strength, 1, sizeof(float), file); + fread(&p_vib_speed, 1, sizeof(float), file); + fread(&p_vib_delay, 1, sizeof(float), file); + + fread(&p_env_attack, 1, sizeof(float), file); + fread(&p_env_sustain, 1, sizeof(float), file); + fread(&p_env_decay, 1, sizeof(float), file); + fread(&p_env_punch, 1, sizeof(float), file); + + fread(&filter_on, 1, sizeof(bool), file); + fread(&p_lpf_resonance, 1, sizeof(float), file); + fread(&p_lpf_freq, 1, sizeof(float), file); + fread(&p_lpf_ramp, 1, sizeof(float), file); + fread(&p_hpf_freq, 1, sizeof(float), file); + fread(&p_hpf_ramp, 1, sizeof(float), file); + + fread(&p_pha_offset, 1, sizeof(float), file); + fread(&p_pha_ramp, 1, sizeof(float), file); + + fread(&p_repeat_speed, 1, sizeof(float), file); + + if(version>=101) + { + fread(&p_arp_speed, 1, sizeof(float), file); + fread(&p_arp_mod, 1, sizeof(float), file); + } + + fclose(file); + return true; +} + +bool SaveSettings(char* filename) +{ + FILE* file=fopen(filename, "wb"); + if(!file) + return false; + + int version=102; + fwrite(&version, 1, sizeof(int), file); + + fwrite(&wave_type, 1, sizeof(int), file); + + fwrite(&sound_vol, 1, sizeof(float), file); + + fwrite(&p_base_freq, 1, sizeof(float), file); + fwrite(&p_freq_limit, 1, sizeof(float), file); + fwrite(&p_freq_ramp, 1, sizeof(float), file); + fwrite(&p_freq_dramp, 1, sizeof(float), file); + fwrite(&p_duty, 1, sizeof(float), file); + fwrite(&p_duty_ramp, 1, sizeof(float), file); + + fwrite(&p_vib_strength, 1, sizeof(float), file); + fwrite(&p_vib_speed, 1, sizeof(float), file); + fwrite(&p_vib_delay, 1, sizeof(float), file); + + fwrite(&p_env_attack, 1, sizeof(float), file); + fwrite(&p_env_sustain, 1, sizeof(float), file); + fwrite(&p_env_decay, 1, sizeof(float), file); + fwrite(&p_env_punch, 1, sizeof(float), file); + + fwrite(&filter_on, 1, sizeof(bool), file); + fwrite(&p_lpf_resonance, 1, sizeof(float), file); + fwrite(&p_lpf_freq, 1, sizeof(float), file); + fwrite(&p_lpf_ramp, 1, sizeof(float), file); + fwrite(&p_hpf_freq, 1, sizeof(float), file); + fwrite(&p_hpf_ramp, 1, sizeof(float), file); + + fwrite(&p_pha_offset, 1, sizeof(float), file); + fwrite(&p_pha_ramp, 1, sizeof(float), file); + + fwrite(&p_repeat_speed, 1, sizeof(float), file); + + fwrite(&p_arp_speed, 1, sizeof(float), file); + fwrite(&p_arp_mod, 1, sizeof(float), file); + + fclose(file); + return true; +} + +void ResetSample(bool restart) +{ + if(!restart) + phase=0; + fperiod=100.0/(p_base_freq*p_base_freq+0.001); + period=(int)fperiod; + fmaxperiod=100.0/(p_freq_limit*p_freq_limit+0.001); + fslide=1.0-pow((double)p_freq_ramp, 3.0)*0.01; + fdslide=-pow((double)p_freq_dramp, 3.0)*0.000001; + square_duty=0.5f-p_duty*0.5f; + square_slide=-p_duty_ramp*0.00005f; + if(p_arp_mod>=0.0f) + arp_mod=1.0-pow((double)p_arp_mod, 2.0)*0.9; + else + arp_mod=1.0+pow((double)p_arp_mod, 2.0)*10.0; + arp_time=0; + arp_limit=(int)(pow(1.0f-p_arp_speed, 2.0f)*20000+32); + if(p_arp_speed==1.0f) + arp_limit=0; + if(!restart) + { + // reset filter + fltp=0.0f; + fltdp=0.0f; + fltw=pow(p_lpf_freq, 3.0f)*0.1f; + fltw_d=1.0f+p_lpf_ramp*0.0001f; + fltdmp=5.0f/(1.0f+pow(p_lpf_resonance, 2.0f)*20.0f)*(0.01f+fltw); + if(fltdmp>0.8f) fltdmp=0.8f; + fltphp=0.0f; + flthp=pow(p_hpf_freq, 2.0f)*0.1f; + flthp_d=1.0+p_hpf_ramp*0.0003f; + // reset vibrato + vib_phase=0.0f; + vib_speed=pow(p_vib_speed, 2.0f)*0.01f; + vib_amp=p_vib_strength*0.5f; + // reset envelope + env_vol=0.0f; + env_stage=0; + env_time=0; + env_length[0]=(int)(p_env_attack*p_env_attack*100000.0f); + env_length[1]=(int)(p_env_sustain*p_env_sustain*100000.0f); + env_length[2]=(int)(p_env_decay*p_env_decay*100000.0f); + + fphase=pow(p_pha_offset, 2.0f)*1020.0f; + if(p_pha_offset<0.0f) fphase=-fphase; + fdphase=pow(p_pha_ramp, 2.0f)*1.0f; + if(p_pha_ramp<0.0f) fdphase=-fdphase; + iphase=abs((int)fphase); + ipp=0; + for(int i=0;i<1024;i++) + phaser_buffer[i]=0.0f; + + for(int i=0;i<32;i++) + noise_buffer[i]=frnd(2.0f)-1.0f; + + rep_time=0; + rep_limit=(int)(pow(1.0f-p_repeat_speed, 2.0f)*20000+32); + if(p_repeat_speed==0.0f) + rep_limit=0; + } +} + +void PlaySample() +{ + ResetSample(false); + playing_sample=true; +} + +void SynthSample(int length, float* buffer, FILE* file) +{ + for(int i=0;i=rep_limit) + { + rep_time=0; + ResetSample(true); + } + + // frequency envelopes/arpeggios + arp_time++; + if(arp_limit!=0 && arp_time>=arp_limit) + { + arp_limit=0; + fperiod*=arp_mod; + } + fslide+=fdslide; + fperiod*=fslide; + if(fperiod>fmaxperiod) + { + fperiod=fmaxperiod; + if(p_freq_limit>0.0f) + playing_sample=false; + } + float rfperiod=fperiod; + if(vib_amp>0.0f) + { + vib_phase+=vib_speed; + rfperiod=fperiod*(1.0+sin(vib_phase)*vib_amp); + } + period=(int)rfperiod; + if(period<8) period=8; + square_duty+=square_slide; + if(square_duty<0.0f) square_duty=0.0f; + if(square_duty>0.5f) square_duty=0.5f; + // volume envelope + env_time++; + if(env_time>env_length[env_stage]) + { + env_time=0; + env_stage++; + if(env_stage==3) + playing_sample=false; + } + if(env_stage==0) + env_vol=(float)env_time/env_length[0]; + if(env_stage==1) + env_vol=1.0f+pow(1.0f-(float)env_time/env_length[1], 1.0f)*2.0f*p_env_punch; + if(env_stage==2) + env_vol=1.0f-(float)env_time/env_length[2]; + + // phaser step + fphase+=fdphase; + iphase=abs((int)fphase); + if(iphase>1023) iphase=1023; + + if(flthp_d!=0.0f) + { + flthp*=flthp_d; + if(flthp<0.00001f) flthp=0.00001f; + if(flthp>0.1f) flthp=0.1f; + } + + float ssample=0.0f; + for(int si=0;si<8;si++) // 8x supersampling + { + float sample=0.0f; + phase++; + if(phase>=period) + { +// phase=0; + phase%=period; + if(wave_type==3) + for(int i=0;i<32;i++) + noise_buffer[i]=frnd(2.0f)-1.0f; + } + // base waveform + float fp=(float)phase/period; + switch(wave_type) + { + case 0: // square + if(fp0.1f) fltw=0.1f; + if(p_lpf_freq!=1.0f) + { + fltdp+=(sample-fltp)*fltw; + fltdp-=fltdp*fltdmp; + } + else + { + fltp=sample; + fltdp=0.0f; + } + fltp+=fltdp; + // hp filter + fltphp+=fltp-pp; + fltphp-=fltphp*flthp; + sample=fltphp; + // phaser + phaser_buffer[ipp&1023]=sample; + sample+=phaser_buffer[(ipp-iphase+1024)&1023]; + ipp=(ipp+1)&1023; + // final accumulation and envelope application + ssample+=sample*env_vol; + } + ssample=ssample/8*master_vol; + + ssample*=2.0f*sound_vol; + + if(buffer!=NULL) + { + if(ssample>1.0f) ssample=1.0f; + if(ssample<-1.0f) ssample=-1.0f; + *buffer++=ssample; + } + if(file!=NULL) + { + // quantize depending on format + // accumulate/count to accomodate variable sample rate? + ssample*=4.0f; // arbitrary gain to get reasonable output volume... + if(ssample>1.0f) ssample=1.0f; + if(ssample<-1.0f) ssample=-1.0f; + filesample+=ssample; + fileacc++; + if(wav_freq==44100 || fileacc==2) + { + filesample/=fileacc; + fileacc=0; + if(wav_bits==16) + { + short isample=(short)(filesample*32000); + fwrite(&isample, 1, 2, file); + } + else + { + unsigned char isample=(unsigned char)(filesample*127+128); + fwrite(&isample, 1, 1, file); + } + filesample=0.0f; + } + file_sampleswritten++; + } + } +} + +DPInput *input; +#ifdef WIN32 +PortAudioStream *stream; +#endif +bool mute_stream; + +#ifdef WIN32 +//ancient portaudio stuff +static int AudioCallback(void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData) +{ + float *out=(float*)outputBuffer; + float *in=(float*)inputBuffer; + (void)outTime; + + if(playing_sample && !mute_stream) + SynthSample(framesPerBuffer, out, NULL); + else + for(int i=0;i 1.0) f = 1.0; + ((Sint16*)stream)[l] = (Sint16)(f * 32767); + } + } + else memset(stream, 0, len); +} +#endif + +bool ExportWAV(char* filename) +{ + FILE* foutput=fopen(filename, "wb"); + if(!foutput) + return false; + // write wav header + char string[32]; + unsigned int dword=0; + unsigned short word=0; + fwrite("RIFF", 4, 1, foutput); // "RIFF" + dword=0; + fwrite(&dword, 1, 4, foutput); // remaining file size + fwrite("WAVE", 4, 1, foutput); // "WAVE" + + fwrite("fmt ", 4, 1, foutput); // "fmt " + dword=16; + fwrite(&dword, 1, 4, foutput); // chunk size + word=1; + fwrite(&word, 1, 2, foutput); // compression code + word=1; + fwrite(&word, 1, 2, foutput); // channels + dword=wav_freq; + fwrite(&dword, 1, 4, foutput); // sample rate + dword=wav_freq*wav_bits/8; + fwrite(&dword, 1, 4, foutput); // bytes/sec + word=wav_bits/8; + fwrite(&word, 1, 2, foutput); // block align + word=wav_bits; + fwrite(&word, 1, 2, foutput); // bits per sample + + fwrite("data", 4, 1, foutput); // "data" + dword=0; + int foutstream_datasize=ftell(foutput); + fwrite(&dword, 1, 4, foutput); // chunk size + + // write sample data + mute_stream=true; + file_sampleswritten=0; + filesample=0.0f; + fileacc=0; + PlaySample(); + while(playing_sample) + SynthSample(256, NULL, foutput); + mute_stream=false; + + // seek back to header and write size info + fseek(foutput, 4, SEEK_SET); + dword=0; + dword=foutstream_datasize-4+file_sampleswritten*wav_bits/8; + fwrite(&dword, 1, 4, foutput); // remaining file size + fseek(foutput, foutstream_datasize, SEEK_SET); + dword=file_sampleswritten*wav_bits/8; + fwrite(&dword, 1, 4, foutput); // chunk size (data) + fclose(foutput); + + return true; +} + +#include "tools.h" + +bool firstframe=true; +int refresh_counter=0; +bool dragOnLeftClick = false; + +bool Slider(int x, int y, float& value, bool bipolar, const char* text) +{ + bool result = false; + if(MouseInBox(x, y, 100, 10)) + { + if(mouse_rightclick) + { + value=0.0f; + result = true; + } + if(mouse_leftclick) + { + if(dragOnLeftClick) + vselected=&value; + else + { + if(bipolar) + value = (mouse_x - x)/50.0f - 1.0f; + else + value = (mouse_x - x)/100.0f; + result = true; + } + } + } + float mv=(float)(mouse_x-mouse_px); + if(vselected!=&value) + mv=0.0f; + if(bipolar) + { + value+=mv*0.005f; + if(value<-1.0f) value=-1.0f; + if(value>1.0f) value=1.0f; + } + else + { + value+=mv*0.0025f; + if(value<0.0f) value=0.0f; + if(value>1.0f) value=1.0f; + } + DrawBar(x-1, y, 102, 10, 0x000000); + int ival=(int)(value*99); + if(bipolar) + ival=(int)(value*49.5f+49.5f); + DrawBar(x, y+1, ival, 8, 0xF0C090); + DrawBar(x+ival, y+1, 100-ival, 8, 0x807060); + DrawBar(x+ival, y+1, 1, 8, 0xFFFFFF); + if(bipolar) + { + DrawBar(x+50, y-1, 1, 3, 0x000000); + DrawBar(x+50, y+8, 1, 3, 0x000000); + } + DWORD tcol=0x000000; + if(wave_type!=0 && (&value==&p_duty || &value==&p_duty_ramp)) + tcol=0x808080; + DrawText(x-4-strlen(text)*8, y+1, tcol, text); + return result; +} + +bool Button(int x, int y, bool highlight, const char* text, int id) +{ + DWORD color1=0x000000; + DWORD color2=0xA09088; + DWORD color3=0x000000; + bool hover=MouseInBox(x, y, 100, 17); + if(hover && mouse_leftclick) + vcurbutton=id; + bool current=(vcurbutton==id); + if(highlight) + { + color1=0x000000; + color2=0x988070; + color3=0xFFF0E0; + } + if(current && hover) + { + color1=0xA09088; + color2=0xFFF0E0; + color3=0xA09088; + } + DrawBar(x-1, y-1, 102, 19, color1); + DrawBar(x, y, 100, 17, color2); + DrawText(x+5, y+5, color3, text); + if(current && hover && !mouse_left) + return true; + return false; +} + +bool ButtonWH(int x, int y, int w, int h, bool highlight, const char* text, int id) +{ + DWORD color1=0x000000; + DWORD color2=0xA09088; + DWORD color3=0x000000; + bool hover=MouseInBox(x, y, w, h); + if(hover && mouse_leftclick) + vcurbutton=id; + bool current=(vcurbutton==id); + if(highlight) + { + color1=0x000000; + color2=0x988070; + color3=0xFFF0E0; + } + if(current && hover) + { + color1=0xA09088; + color2=0xFFF0E0; + color3=0xA09088; + } + DrawBar(x-1, y-1, w + 2, h + 2, color1); + DrawBar(x, y, w, h, color2); + DrawText(x+5, y+5, color3, text); + if(current && hover && !mouse_left) + return true; + return false; +} + +int drawcount=0; + +void DrawScreen() +{ + bool redraw=true; + if(!firstframe && mouse_x-mouse_px==0 && mouse_y-mouse_py==0 && !mouse_left && !mouse_right) + redraw=false; + if(!mouse_left) + { + if(vselected!=NULL || vcurbutton>-1) + { + redraw=true; + refresh_counter=2; + } + vselected=NULL; + } + if(refresh_counter>0) + { + refresh_counter--; + redraw=true; + } + + if(playing_sample) + redraw=true; + + if(drawcount++>20) + { + redraw=true; + drawcount=0; + } + + if(!redraw) + return; + + firstframe=false; + + ddkLock(); + + ClearScreen(0xC0B090); + + DrawText(10, 10, 0x504030, "GENERATOR"); + for(int i=0;i<7;i++) + { + if(Button(5, 35+i*30, false, categories[i].name, 300+i)) + { + switch(i) + { + case 0: // pickup/coin + ResetParams(); + p_base_freq=0.4f+frnd(0.5f); + p_env_attack=0.0f; + p_env_sustain=frnd(0.1f); + p_env_decay=0.1f+frnd(0.4f); + p_env_punch=0.3f+frnd(0.3f); + if(rnd(1)) + { + p_arp_speed=0.5f+frnd(0.2f); + p_arp_mod=0.2f+frnd(0.4f); + } + break; + case 1: // laser/shoot + ResetParams(); + wave_type=rnd(2); + if(wave_type==2 && rnd(1)) + wave_type=rnd(1); + p_base_freq=0.5f+frnd(0.5f); + p_freq_limit=p_base_freq-0.2f-frnd(0.6f); + if(p_freq_limit<0.2f) p_freq_limit=0.2f; + p_freq_ramp=-0.15f-frnd(0.2f); + if(rnd(2)==0) + { + p_base_freq=0.3f+frnd(0.6f); + p_freq_limit=frnd(0.1f); + p_freq_ramp=-0.35f-frnd(0.3f); + } + if(rnd(1)) + { + p_duty=frnd(0.5f); + p_duty_ramp=frnd(0.2f); + } + else + { + p_duty=0.4f+frnd(0.5f); + p_duty_ramp=-frnd(0.7f); + } + p_env_attack=0.0f; + p_env_sustain=0.1f+frnd(0.2f); + p_env_decay=frnd(0.4f); + if(rnd(1)) + p_env_punch=frnd(0.3f); + if(rnd(2)==0) + { + p_pha_offset=frnd(0.2f); + p_pha_ramp=-frnd(0.2f); + } + if(rnd(1)) + p_hpf_freq=frnd(0.3f); + break; + case 2: // explosion + ResetParams(); + wave_type=3; + if(rnd(1)) + { + p_base_freq=0.1f+frnd(0.4f); + p_freq_ramp=-0.1f+frnd(0.4f); + } + else + { + p_base_freq=0.2f+frnd(0.7f); + p_freq_ramp=-0.2f-frnd(0.2f); + } + p_base_freq*=p_base_freq; + if(rnd(4)==0) + p_freq_ramp=0.0f; + if(rnd(2)==0) + p_repeat_speed=0.3f+frnd(0.5f); + p_env_attack=0.0f; + p_env_sustain=0.1f+frnd(0.3f); + p_env_decay=frnd(0.5f); + if(rnd(1)==0) + { + p_pha_offset=-0.3f+frnd(0.9f); + p_pha_ramp=-frnd(0.3f); + } + p_env_punch=0.2f+frnd(0.6f); + if(rnd(1)) + { + p_vib_strength=frnd(0.7f); + p_vib_speed=frnd(0.6f); + } + if(rnd(2)==0) + { + p_arp_speed=0.6f+frnd(0.3f); + p_arp_mod=0.8f-frnd(1.6f); + } + break; + case 3: // powerup + ResetParams(); + if(rnd(1)) + wave_type=1; + else + p_duty=frnd(0.6f); + if(rnd(1)) + { + p_base_freq=0.2f+frnd(0.3f); + p_freq_ramp=0.1f+frnd(0.4f); + p_repeat_speed=0.4f+frnd(0.4f); + } + else + { + p_base_freq=0.2f+frnd(0.3f); + p_freq_ramp=0.05f+frnd(0.2f); + if(rnd(1)) + { + p_vib_strength=frnd(0.7f); + p_vib_speed=frnd(0.6f); + } + } + p_env_attack=0.0f; + p_env_sustain=frnd(0.4f); + p_env_decay=0.1f+frnd(0.4f); + break; + case 4: // hit/hurt + ResetParams(); + wave_type=rnd(2); + if(wave_type==2) + wave_type=3; + if(wave_type==0) + p_duty=frnd(0.6f); + p_base_freq=0.2f+frnd(0.6f); + p_freq_ramp=-0.3f-frnd(0.4f); + p_env_attack=0.0f; + p_env_sustain=frnd(0.1f); + p_env_decay=0.1f+frnd(0.2f); + if(rnd(1)) + p_hpf_freq=frnd(0.3f); + break; + case 5: // jump + ResetParams(); + wave_type=0; + p_duty=frnd(0.6f); + p_base_freq=0.3f+frnd(0.3f); + p_freq_ramp=0.1f+frnd(0.2f); + p_env_attack=0.0f; + p_env_sustain=0.1f+frnd(0.3f); + p_env_decay=0.1f+frnd(0.2f); + if(rnd(1)) + p_hpf_freq=frnd(0.3f); + if(rnd(1)) + p_lpf_freq=1.0f-frnd(0.6f); + break; + case 6: // blip/select + ResetParams(); + wave_type=rnd(1); + if(wave_type==0) + p_duty=frnd(0.6f); + p_base_freq=0.2f+frnd(0.4f); + p_env_attack=0.0f; + p_env_sustain=0.1f+frnd(0.1f); + p_env_decay=frnd(0.2f); + p_hpf_freq=0.1f; + break; + default: + break; + } + + PlaySample(); + } + } + DrawBar(110, 0, 2, 480, 0x000000); + DrawText(120, 10, 0x504030, "MANUAL SETTINGS"); + DrawSprite(ld48, 8, 440, 0, 0xB0A080); + + + bool do_play=false; + + if(Button(130, 30, wave_type==0, "SQUAREWAVE", 10)) + { + wave_type=0; + do_play=true; + } + if(Button(250, 30, wave_type==1, "SAWTOOTH", 11)) + { + wave_type=1; + do_play=true; + } + if(Button(370, 30, wave_type==2, "SINEWAVE", 12)) + { + wave_type=2; + do_play=true; + } + if(Button(490, 30, wave_type==3, "NOISE", 13)) + { + wave_type=3; + do_play=true; + } + if(dragOnLeftClick) + { + if(ButtonWH(490, 140, 17, 17, dragOnLeftClick, "X", 101)) + dragOnLeftClick = !dragOnLeftClick; + } + else + { + if(ButtonWH(490, 140, 17, 17, dragOnLeftClick, "", 101)) + dragOnLeftClick = !dragOnLeftClick; + } + DrawText(515, 145, 0x000000, "DRAG BARS"); + + DrawBar(5-1-1, 412-1-1, 102+2, 19+2, 0x000000); + if(Button(5, 412, false, "RANDOMIZE", 40)) + { + p_base_freq=pow(frnd(2.0f)-1.0f, 2.0f); + if(rnd(1)) + p_base_freq=pow(frnd(2.0f)-1.0f, 3.0f)+0.5f; + p_freq_limit=0.0f; + p_freq_ramp=pow(frnd(2.0f)-1.0f, 5.0f); + if(p_base_freq>0.7f && p_freq_ramp>0.2f) + p_freq_ramp=-p_freq_ramp; + if(p_base_freq<0.2f && p_freq_ramp<-0.05f) + p_freq_ramp=-p_freq_ramp; + p_freq_dramp=pow(frnd(2.0f)-1.0f, 3.0f); + p_duty=frnd(2.0f)-1.0f; + p_duty_ramp=pow(frnd(2.0f)-1.0f, 3.0f); + p_vib_strength=pow(frnd(2.0f)-1.0f, 3.0f); + p_vib_speed=frnd(2.0f)-1.0f; + p_vib_delay=frnd(2.0f)-1.0f; + p_env_attack=pow(frnd(2.0f)-1.0f, 3.0f); + p_env_sustain=pow(frnd(2.0f)-1.0f, 2.0f); + p_env_decay=frnd(2.0f)-1.0f; + p_env_punch=pow(frnd(0.8f), 2.0f); + if(p_env_attack+p_env_sustain+p_env_decay<0.2f) + { + p_env_sustain+=0.2f+frnd(0.3f); + p_env_decay+=0.2f+frnd(0.3f); + } + p_lpf_resonance=frnd(2.0f)-1.0f; + p_lpf_freq=1.0f-pow(frnd(1.0f), 3.0f); + p_lpf_ramp=pow(frnd(2.0f)-1.0f, 3.0f); + if(p_lpf_freq<0.1f && p_lpf_ramp<-0.05f) + p_lpf_ramp=-p_lpf_ramp; + p_hpf_freq=pow(frnd(1.0f), 5.0f); + p_hpf_ramp=pow(frnd(2.0f)-1.0f, 5.0f); + p_pha_offset=pow(frnd(2.0f)-1.0f, 3.0f); + p_pha_ramp=pow(frnd(2.0f)-1.0f, 3.0f); + p_repeat_speed=frnd(2.0f)-1.0f; + p_arp_speed=frnd(2.0f)-1.0f; + p_arp_mod=frnd(2.0f)-1.0f; + do_play=true; + } + + if(ButtonWH(5, 352, 75, 17, false, "UNDO (Z)", 102)) + { + Undo(); + } + if(Button(5, 382, false, "MUTATE", 30)) + { + if(rnd(1)) p_base_freq+=frnd(0.1f)-0.05f; +// if(rnd(1)) p_freq_limit+=frnd(0.1f)-0.05f; + if(rnd(1)) p_freq_ramp+=frnd(0.1f)-0.05f; + if(rnd(1)) p_freq_dramp+=frnd(0.1f)-0.05f; + if(rnd(1)) p_duty+=frnd(0.1f)-0.05f; + if(rnd(1)) p_duty_ramp+=frnd(0.1f)-0.05f; + if(rnd(1)) p_vib_strength+=frnd(0.1f)-0.05f; + if(rnd(1)) p_vib_speed+=frnd(0.1f)-0.05f; + if(rnd(1)) p_vib_delay+=frnd(0.1f)-0.05f; + if(rnd(1)) p_env_attack+=frnd(0.1f)-0.05f; + if(rnd(1)) p_env_sustain+=frnd(0.1f)-0.05f; + if(rnd(1)) p_env_decay+=frnd(0.1f)-0.05f; + if(rnd(1)) p_env_punch+=frnd(0.1f)-0.05f; + if(rnd(1)) p_lpf_resonance+=frnd(0.1f)-0.05f; + if(rnd(1)) p_lpf_freq+=frnd(0.1f)-0.05f; + if(rnd(1)) p_lpf_ramp+=frnd(0.1f)-0.05f; + if(rnd(1)) p_hpf_freq+=frnd(0.1f)-0.05f; + if(rnd(1)) p_hpf_ramp+=frnd(0.1f)-0.05f; + if(rnd(1)) p_pha_offset+=frnd(0.1f)-0.05f; + if(rnd(1)) p_pha_ramp+=frnd(0.1f)-0.05f; + if(rnd(1)) p_repeat_speed+=frnd(0.1f)-0.05f; + if(rnd(1)) p_arp_speed+=frnd(0.1f)-0.05f; + if(rnd(1)) p_arp_mod+=frnd(0.1f)-0.05f; + do_play=true; + } + + DrawText(515, 170, 0x000000, "VOLUME"); + DrawBar(490-1-1+60, 180-1+5, 70, 2, 0x000000); + DrawBar(490-1-1+60+68, 180-1+5, 2, 205, 0x000000); + DrawBar(490-1-1+60, 180-1, 42+2, 10+2, 0xFF0000); + if(Slider(490, 180, sound_vol, false, " ")) + PlaySample(); + if(Button(490, 200, false, "PLAY SOUND", 20)) + PlaySample(); + + if(Button(490, 290, false, "LOAD SOUND", 14)) + { + char filename[256]; + if(FileSelectorLoad(hWndMain, filename, 1)) // WIN32 + { + ResetParams(); + LoadSettings(filename); + PlaySample(); + } + } + if(Button(490, 320, false, "SAVE SOUND", 15)) + { + char filename[256]; + if(FileSelectorSave(hWndMain, filename, 1)) // WIN32 + SaveSettings(filename); + } + + DrawBar(490-1-1+60, 380-1+9, 70, 2, 0x000000); + DrawBar(490-1-2, 380-1-2, 102+4, 19+4, 0x000000); + if(Button(490, 380, false, "EXPORT .WAV", 16)) + { + char filename[256]; + if(FileSelectorSave(hWndMain, filename, 0)) // WIN32 + ExportWAV(filename); + } + char str[10]; + sprintf(str, "%i HZ", wav_freq); + if(Button(490, 410, false, str, 18)) + { + if(wav_freq==44100) + wav_freq=22050; + else + wav_freq=44100; + } + sprintf(str, "%i-BIT", wav_bits); + if(Button(490, 440, false, str, 19)) + { + if(wav_bits==16) + wav_bits=8; + else + wav_bits=16; + } + + int ypos=4; + + int xpos=350; + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + float oldValue = 0; + + oldValue = p_env_attack; + if(Slider(xpos, (ypos++)*18, p_env_attack, false, "ATTACK TIME")) + { + do_play = true; + SetUndo(&p_env_attack, oldValue); + } + oldValue = p_env_sustain; + if(Slider(xpos, (ypos++)*18, p_env_sustain, false, "SUSTAIN TIME")) + { + do_play = true; + SetUndo(&p_env_sustain, oldValue); + } + oldValue = p_env_punch; + if(Slider(xpos, (ypos++)*18, p_env_punch, false, "SUSTAIN PUNCH")) + { + do_play = true; + SetUndo(&p_env_punch, oldValue); + } + oldValue = p_env_decay; + if(Slider(xpos, (ypos++)*18, p_env_decay, false, "DECAY TIME")) + { + do_play = true; + SetUndo(&p_env_decay, oldValue); + } + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + oldValue = p_base_freq; + if(Slider(xpos, (ypos++)*18, p_base_freq, false, "START FREQUENCY")) + { + do_play = true; + SetUndo(&p_base_freq, oldValue); + } + oldValue = p_freq_limit; + if(Slider(xpos, (ypos++)*18, p_freq_limit, false, "MIN FREQUENCY")) + { + do_play = true; + SetUndo(&p_freq_limit, oldValue); + } + oldValue = p_freq_ramp; + if(Slider(xpos, (ypos++)*18, p_freq_ramp, true, "SLIDE")) + { + do_play = true; + SetUndo(&p_freq_ramp, oldValue); + } + oldValue = p_freq_dramp; + if(Slider(xpos, (ypos++)*18, p_freq_dramp, true, "DELTA SLIDE")) + { + do_play = true; + SetUndo(&p_freq_dramp, oldValue); + } + + oldValue = p_vib_strength; + if(Slider(xpos, (ypos++)*18, p_vib_strength, false, "VIBRATO DEPTH")) + { + do_play = true; + SetUndo(&p_vib_strength, oldValue); + } + oldValue = p_vib_speed; + if(Slider(xpos, (ypos++)*18, p_vib_speed, false, "VIBRATO SPEED")) + { + do_play = true; + SetUndo(&p_vib_speed, oldValue); + } + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + oldValue = p_arp_mod; + if(Slider(xpos, (ypos++)*18, p_arp_mod, true, "CHANGE AMOUNT")) + { + do_play = true; + SetUndo(&p_arp_mod, oldValue); + } + oldValue = p_arp_speed; + if(Slider(xpos, (ypos++)*18, p_arp_speed, false, "CHANGE SPEED")) + { + do_play = true; + SetUndo(&p_arp_speed, oldValue); + } + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + oldValue = p_duty; + if(Slider(xpos, (ypos++)*18, p_duty, false, "SQUARE DUTY")) + { + do_play = true; + SetUndo(&p_duty, oldValue); + } + oldValue = p_duty_ramp; + if(Slider(xpos, (ypos++)*18, p_duty_ramp, true, "DUTY SWEEP")) + { + do_play = true; + SetUndo(&p_duty_ramp, oldValue); + } + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + oldValue = p_repeat_speed; + if(Slider(xpos, (ypos++)*18, p_repeat_speed, false, "REPEAT SPEED")) + { + do_play = true; + SetUndo(&p_repeat_speed, oldValue); + } + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + oldValue = p_pha_offset; + if(Slider(xpos, (ypos++)*18, p_pha_offset, true, "PHASER OFFSET")) + { + do_play = true; + SetUndo(&p_pha_offset, oldValue); + } + oldValue = p_pha_ramp; + if(Slider(xpos, (ypos++)*18, p_pha_ramp, true, "PHASER SWEEP")) + { + do_play = true; + SetUndo(&p_pha_ramp, oldValue); + } + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + oldValue = p_lpf_freq; + if(Slider(xpos, (ypos++)*18, p_lpf_freq, false, "LP FILTER CUTOFF")) + { + do_play = true; + SetUndo(&p_lpf_freq, oldValue); + } + oldValue = p_lpf_ramp; + if(Slider(xpos, (ypos++)*18, p_lpf_ramp, true, "LP FILTER CUTOFF SWEEP")) + { + do_play = true; + SetUndo(&p_lpf_ramp, oldValue); + } + oldValue = p_lpf_resonance; + if(Slider(xpos, (ypos++)*18, p_lpf_resonance, false, "LP FILTER RESONANCE")) + { + do_play = true; + SetUndo(&p_lpf_resonance, oldValue); + } + oldValue = p_hpf_freq; + if(Slider(xpos, (ypos++)*18, p_hpf_freq, false, "HP FILTER CUTOFF")) + { + do_play = true; + SetUndo(&p_hpf_freq, oldValue); + } + oldValue = p_hpf_ramp; + if(Slider(xpos, (ypos++)*18, p_hpf_ramp, true, "HP FILTER CUTOFF SWEEP")) + { + do_play = true; + SetUndo(&p_hpf_ramp, oldValue); + } + + DrawBar(xpos-190, ypos*18-5, 300, 2, 0x0000000); + + DrawBar(xpos-190, 4*18-5, 1, (ypos-4)*18, 0x0000000); + DrawBar(xpos-190+299, 4*18-5, 1, (ypos-4)*18, 0x0000000); + + if(do_play) + PlaySample(); + + ddkUnlock(); + + if(!mouse_left) + vcurbutton=-1; +} + +bool keydown=false; + +bool ddkCalcFrame() +{ + input->Update(); // WIN32 (for keyboard input) + + if(input->KeyPressed(DIK_SPACE) || input->KeyPressed(DIK_RETURN)) // WIN32 (keyboard input only for convenience, ok to remove) + { + if(!keydown) + { + PlaySample(); + keydown=true; + } + } + else if(input->KeyPressed(DIK_Z)) + { + Undo(); + keydown=true; + } + else + keydown=false; + + DrawScreen(); + + Sleep(5); // WIN32 + return true; +} + +void ddkInit() +{ + srand(time(NULL)); + + ddkSetMode(640,480, 32, 60, DDK_WINDOW, "sfxr"); // requests window size etc from ddrawkit + + if (LoadTGA(font, "/usr/share/sfxr/font.tga")) { + /* Try again in cwd */ + if (LoadTGA(font, "font.tga")) { + fprintf(stderr, + "Error could not open /usr/share/sfxr/font.tga" + " nor font.tga\n"); + exit(1); + } + } + + if (LoadTGA(ld48, "/usr/share/sfxr/ld48.tga")) { + /* Try again in cwd */ + if (LoadTGA(ld48, "ld48.tga")) { + fprintf(stderr, + "Error could not open /usr/share/sfxr/ld48.tga" + " nor ld48.tga\n"); + exit(1); + } + } + + ld48.width=ld48.pitch; + + input=new DPInput(hWndMain, hInstanceMain); // WIN32 + + strcpy(categories[0].name, "PICKUP/COIN"); + strcpy(categories[1].name, "LASER/SHOOT"); + strcpy(categories[2].name, "EXPLOSION"); + strcpy(categories[3].name, "POWERUP"); + strcpy(categories[4].name, "HIT/HURT"); + strcpy(categories[5].name, "JUMP"); + strcpy(categories[6].name, "BLIP/SELECT"); + + ResetParams(); + +#ifdef WIN32 + // Init PortAudio + SetEnvironmentVariable("PA_MIN_LATENCY_MSEC", "75"); // WIN32 + Pa_Initialize(); + Pa_OpenDefaultStream( + &stream, + 0, + 1, + paFloat32, // output type + 44100, + 512, // samples per buffer + 0, // # of buffers + AudioCallback, + NULL); + Pa_StartStream(stream); +#else + SDL_AudioSpec des; + des.freq = 44100; + des.format = AUDIO_S16SYS; + des.channels = 1; + des.samples = 512; + des.callback = SDLAudioCallback; + des.userdata = NULL; + VERIFY(!SDL_OpenAudio(&des, NULL)); + SDL_PauseAudio(0); +#endif +} + +void ddkFree() +{ + delete input; + free(ld48.data); + free(font.data); + +#ifdef WIN32 + // Close PortAudio + Pa_StopStream(stream); + Pa_CloseStream(stream); + Pa_Terminate(); +#endif +} + diff --git a/sfxr/readme.txt b/sfxr/readme.txt new file mode 100644 index 0000000..a5b2fd6 --- /dev/null +++ b/sfxr/readme.txt @@ -0,0 +1,163 @@ +----------------------------- +sfxr - sound effect generator +----------------------------- + by DrPetter, 2007-12-14 + developed for LD48#10 +----------------------------- + + +Basic usage: + +Start the application, then hit +some of the buttons on the left +side to generate random sounds +matching the button descriptions. + +Press "Export .WAV" to save the +current sound as a WAV audio file. +Click the buttons below to change +WAV format in terms of bits per +sample and sample rate. + +If you find a sound that is sort +of interesting but not quite what +you want, you can drag some sliders +around until it sounds better. + +The Randomize button generates +something completely random. + +Mutate slightly alters the current +parameters to automatically create +a variation of the sound. + + + +Advanced usage: + +Figure out what each slider does and +use them to adjust particular aspects +of the current sound... + +Press the right mouse button on a slider +to reset it to a value of zero. + +Press Space or Enter to play the current sound. + +The Save/Load sound buttons allow saving +and loading of program parameters to work +on a sound over several sessions. + +Volume setting is saved with the sound and +exported to WAV. If you increase it too much +there's a risk of clipping. + +Some parameters influence the sound during +playback (particularly when using a non-zero +repeat speed), and dragging these sliders +can cause some interesting effects. +To record this you will need to use an external +recording application, for instance Audacity. +Set the recording source in that application +to "Wave", "Stereo Mix", "Mixed Output" or similar. + +Using an external sound editor to capture and edit +sound can also be used to string several sounds +together for more complex results. + +Parameter description: +- The top four buttons select base waveform +- First four parameters control the volume envelope + Attack is the beginning of the sound, + longer attack means a smoother start. + Sustain is how long the volume is held constant + before fading out. + Increase Sustain Punch to cause a popping + effect with increased (and falling) volume + during the sustain phase. + Decay is the fade-out time. +- Next six are for controlling the sound pitch or + frequency. + Start frequency is pretty obvious. Has a large + impact on the overall sound. + Min frequency represents a cutoff that stops all + sound if it's passed during a downward slide. + Slide sets the speed at which the frequency should + be swept (up or down). + Delta slide is the "slide of slide", or rate of change + in the slide speed. + Vibrato depth/speed makes for an oscillating + frequency effect at various strengths and rates. +- Then we have two parameters for causing an abrupt + change in pitch after a ceratin delay. + Amount is pitch change (up or down) + and Speed indicates time to wait before changing + the pitch. +- Following those are two parameters specific to the + squarewave waveform. + The duty cycle of a square describes its shape + in terms of how large the positive vs negative + sections are. It can be swept up or down by + changing the second parameter. +- Repeat speed, when not zero, causes the frequency + and duty parameters to be reset at regular intervals + while the envelope and filter continue unhindered. + This can make for some interesting pulsating effects. +- Phaser offset overlays a delayed copy of the audio + stream on top of itself, resulting in a kind of tight + reverb or sci-fi effect. + This parameter can also be swept like many others. +- Finally, the bottom five sliders control two filters + which are applied after all other effects. + The first one is a resonant lowpass filter which has + a sweepable cutoff frequency. + The other is a highpass filter which can be used to + remove undesired low frequency hum in "light" sounds. + + +---------------------- + + +License +------- + +Basically, I don't care what you do with it, anything goes. + +To please all the troublesome folks who request a formal license, +I attach the "MIT license" as follows: + +-- + +Copyright (c) 2007 Tomas Pettersson + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + + +---------------------- + +http://www.drpetter.se + + drpetter@gmail.com + +---------------------- + diff --git a/sfxr/sdlkit.h b/sfxr/sdlkit.h new file mode 100644 index 0000000..df79f76 --- /dev/null +++ b/sfxr/sdlkit.h @@ -0,0 +1,174 @@ +#ifndef SDLKIT_H +#define SDLKIT_H + +#include "SDL.h" +#define ERROR(x) error(__FILE__, __LINE__, #x) +#define VERIFY(x) do { if (!(x)) ERROR(x); } while (0) +#include +#include + +static void error (const char *file, unsigned int line, const char *msg) +{ + fprintf(stderr, "[!] %s:%u %s\n", file, line, msg); + exit(1); +} + +typedef Uint32 DWORD; +typedef Uint16 WORD; + +#define DIK_SPACE SDLK_SPACE +#define DIK_RETURN SDLK_RETURN +#define DIK_Z SDLK_z +#define DDK_WINDOW 0 + +#define hWndMain 0 +#define hInstanceMain 0 + +#define Sleep(x) SDL_Delay(x) + +static bool keys[SDLK_LAST]; + +void ddkInit(); // Will be called on startup +bool ddkCalcFrame(); // Will be called every frame, return true to continue running or false to quit +void ddkFree(); // Will be called on shutdown + +class DPInput { +public: + DPInput(int,int) {} + ~DPInput() {} + static void Update () {} + + static bool KeyPressed(SDLKey key) + { + bool r = keys[key]; + keys[key] = false; + return r; + } + +}; + +static Uint32 *ddkscreen32; +static Uint16 *ddkscreen16; +static int ddkpitch; +static int mouse_x, mouse_y, mouse_px, mouse_py; +static bool mouse_left = false, mouse_right = false, mouse_middle = false; +static bool mouse_leftclick = false, mouse_rightclick = false, mouse_middleclick = false; + +static SDL_Surface *sdlscreen = NULL; + +static void sdlupdate () +{ + mouse_px = mouse_x; + mouse_py = mouse_y; + Uint8 buttons = SDL_GetMouseState(&mouse_x, &mouse_y); + bool mouse_left_p = mouse_left; + bool mouse_right_p = mouse_right; + bool mouse_middle_p = mouse_middle; + mouse_left = buttons & SDL_BUTTON(1); + mouse_right = buttons & SDL_BUTTON(3); + mouse_middle = buttons & SDL_BUTTON(2); + mouse_leftclick = mouse_left && !mouse_left_p; + mouse_rightclick = mouse_right && !mouse_right_p; + mouse_middleclick = mouse_middle && !mouse_middle_p; +} + +static bool ddkLock () +{ + SDL_LockSurface(sdlscreen); + ddkpitch = sdlscreen->pitch / (sdlscreen->format->BitsPerPixel == 32 ? 4 : 2); + ddkscreen16 = (Uint16*)(sdlscreen->pixels); + ddkscreen32 = (Uint32*)(sdlscreen->pixels); +} + +static void ddkUnlock () +{ + SDL_UnlockSurface(sdlscreen); +} + +static void ddkSetMode (int width, int height, int bpp, int refreshrate, int fullscreen, const char *title) +{ + VERIFY(sdlscreen = SDL_SetVideoMode(width, height, bpp, fullscreen ? SDL_FULLSCREEN : 0)); + SDL_WM_SetCaption(title, title); +} + +#include +#include +#include + +static char *gtkfilename; + +static void selected_file (GtkWidget *button, GtkFileSelection *fs) +{ + strncpy(gtkfilename, gtk_file_selection_get_filename(fs), 255); + gtkfilename[255] = 0; + gtk_widget_destroy(GTK_WIDGET(fs)); + gtk_main_quit(); +} + +static bool select_file (char *buf) +{ + gtkfilename = buf; + GtkWidget *dialog = gtk_file_selection_new("It's file selection time!"); + g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(gtk_main_quit), NULL); + g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(dialog)->ok_button), "clicked", G_CALLBACK(selected_file), G_OBJECT(dialog)); + g_signal_connect_swapped(G_OBJECT(GTK_FILE_SELECTION(dialog)->cancel_button), "clicked", G_CALLBACK(gtk_widget_destroy), G_OBJECT(dialog)); + gtk_widget_show(GTK_WIDGET(dialog)); + gtk_main(); + return *gtkfilename; +} + +#define FileSelectorLoad(x,file,y) select_file(file) +#define FileSelectorSave(x,file,y) select_file(file) + +static void sdlquit () +{ + ddkFree(); + SDL_Quit(); +} + +static void sdlinit () +{ + SDL_Surface *icon; + VERIFY(!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)); + icon = SDL_LoadBMP("/usr/share/sfxr/sfxr.bmp"); + if (!icon) + icon = SDL_LoadBMP("sfxr.bmp"); + if (icon) + SDL_WM_SetIcon(icon, NULL); + atexit(sdlquit); + memset(keys, 0, sizeof(keys)); + ddkInit(); +} + +static void loop (void) +{ + SDL_Event e; + while (true) + { + SDL_PollEvent(&e); + switch (e.type) + { + case SDL_QUIT: + return; + + case SDL_KEYDOWN: + keys[e.key.keysym.sym] = true; + + default: break; + } + sdlupdate(); + if (!ddkCalcFrame()) + return; + SDL_Flip(sdlscreen); + } +} + +int main(int argc, char *argv[]) +{ + gtk_init(&argc, &argv); + sdlinit(); + loop(); + return 0; +} + +#endif diff --git a/sfxr/sfxr b/sfxr/sfxr new file mode 100755 index 0000000..bb905c9 Binary files /dev/null and b/sfxr/sfxr differ diff --git a/sfxr/sfxr.bmp b/sfxr/sfxr.bmp new file mode 100644 index 0000000..55e7d66 Binary files /dev/null and b/sfxr/sfxr.bmp differ diff --git a/sfxr/sfxr.desktop b/sfxr/sfxr.desktop new file mode 100644 index 0000000..e87f3b8 --- /dev/null +++ b/sfxr/sfxr.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=sfxr +Comment=Generate sound effects +Exec=sfxr +Icon=sfxr +Terminal=false +StartupNotify=false +Type=Application +Categories=AudioVideo;Audio;AudioVideoEditing; diff --git a/sfxr/sfxr.png b/sfxr/sfxr.png new file mode 100644 index 0000000..7209119 Binary files /dev/null and b/sfxr/sfxr.png differ diff --git a/sfxr/tools.h b/sfxr/tools.h new file mode 100644 index 0000000..6ecf243 --- /dev/null +++ b/sfxr/tools.h @@ -0,0 +1,134 @@ +int LoadTGA(Spriteset& tiles, const char *filename) +{ + FILE *file; + unsigned char byte, crap[16], id_length; + int n, width, height, channels, x, y; + file=fopen(filename, "rb"); + if (!file) + return -1; + fread(&id_length, 1, 1, file); + fread(crap, 1, 11, file); + width=0; + height=0; + fread(&width, 1, 2, file); // width + fread(&height, 1, 2, file); // height + fread(&byte, 1, 1, file); // bits + channels=byte/8; + fread(&byte, 1, 1, file); // image descriptor byte (per-bit info) + for(n=0;n=0;y--) + for(x=0;x8) + for(x1=0;x1=x && mouse_x=y && mouse_y