/*************************************************************************** **************************************************************************** **************************************************************************** * * FunktrackerGOLD - By Jason Nunn * Copyright (C) 1996,1998 Jason Nunn * * FunktrackerGOLD now comes under the GNU General Public License. Please * read the COPYING notice in this distribution. * * ================================================================ * The main file. * * ========================================================= * * miwi: Changed to read maxy from ncurses LINES, and fill out the xterm * in y-direction. x-direction was not changed because the columns do * not scroll anyway. bug: resizing while running is not noticed. * Michael.Will@student.uni-tuebingen.de, May, 27th, 1996 * **************************************************************************** **************************************************************************** ***************************************************************************/ #include #include #include #include #include #include #include "funktracker_defs.h" #include "dsp_mixxer.h" #include "funktracker.h" #include "funkload.h" #include "funkgold_misc.h" #include "funkgold_dir.h" #include "funkgold_sm.h" #include "funkgold_se.h" #include "funkgold_pe.h" #include "funkgold_trac.h" int ch,maxy,has_colours = 0; char song_directory[1024]; char sample_directory[1024]; char song_filename[256]; /*posix length ?*/ char sam_filename[256]; unsigned char song_fpos_real = 0; unsigned char song_fpos_hl = 0; unsigned char song_fpos_hl_old = 0; unsigned char sam_fpos_real = 0; unsigned char sam_fpos_hl = 0; unsigned char sam_fpos_hl_old = 0; char edit_mode; #define SONGNAME_SIZE 50 char filename[SONGNAME_SIZE + 1]; char menu_option = 0; char nibble_display[] = "0123456789ABCDEF"; char *note_display[] = { "C-1","C#1","D-1","D#1","E-1","F-1","F#1","G-1","G#1","A-1","A#1","B-1", "C-2","C#2","D-2","D#2","E-2","F-2","F#2","G-2","G#2","A-2","A#2","B-2", "C-3","C#3","D-3","D#3","E-3","F-3","F#3","G-3","G#3","A-3","A#3","B-3", "C-4","C#4","D-4","D#4","E-4","F-4","F#4","G-4","G#4","A-4","A#4","B-4", "C-5","C#5","D-5","D#5","E-5","F-5","F#5","G-5","G#5","A-5","A#5","B-5", "???","RLO","***"," " }; /*************************************************************************** * ***************************************************************************/ void init_colour_pairs(void) { init_pair(COL_NOTHING,COLOR_BLACK,COLOR_BLACK); init_pair(COL_TOPBAR,COL_TOPBAR_FG,COL_TOPBAR_BG); init_pair(COL_BOX,COL_BOX_FG,COL_BOX_BG); init_pair(COL_FERR_MESSAGE,COL_FERR_MESSAGE_FG,COL_FERR_MESSAGE_BG); init_pair(COL_TITLE,COL_TITLE_FG,COL_TITLE_BG); init_pair(COL_TEXT,COL_TEXT_FG,COL_TEXT_BG); init_pair(COL_TEXT2,COL_TEXT2_FG,COL_TEXT2_BG); init_pair(COL_BAR,COL_BAR_FG,COL_BAR_BG); if(has_colors()) has_colours = 1; } void set_colour_hl(int colour) { if(has_colours) attrset(COLOR_PAIR(colour) | A_BOLD); else attron(A_REVERSE); } void set_colour(int colour) { if(has_colours) attrset(COLOR_PAIR(colour)); else attroff(A_REVERSE); } /*************************************************************************** * ***************************************************************************/ void display_topbar(void) { set_colour(COL_NOTHING); clear(); set_colour_hl(COL_TOPBAR); move(0,0); addstr(" FunktrackerGOLD " FUNK_VERSION " " "Copyright (C) 1994,1996,1998 Jason Nunn "); } /*************************************************************************** * Utility functions ***************************************************************************/ /************************************ * ************************************/ void hl_bar(int option,int x) { if(option == x) attron(A_REVERSE); else attroff(A_REVERSE); } void update_screen(void) { attroff(A_REVERSE); move(0,0); refresh(); } /************************************ * ************************************/ void byte2str(char *str,unsigned char value) { *str = nibble_display[value >> 4]; *(str + 1) = nibble_display[value & 0x0f]; } void word2str(char *str,unsigned long value) { *str = nibble_display[(value >> 12) & 0xf]; *(str + 1) = nibble_display[(value >> 8) & 0xf]; *(str + 2) = nibble_display[(value >> 4) & 0xf]; *(str + 3) = nibble_display[value & 0xf]; } void dword2str(char *str,unsigned long value) { *str = nibble_display[(value >> 28) & 0xf]; *(str + 1) = nibble_display[(value >> 24) & 0xf]; *(str + 2) = nibble_display[(value >> 20) & 0xf]; *(str + 3) = nibble_display[(value >> 16) & 0xf]; *(str + 4) = nibble_display[(value >> 12) & 0xf]; *(str + 5) = nibble_display[(value >> 8) & 0xf]; *(str + 6) = nibble_display[(value >> 4) & 0xf]; *(str + 7) = nibble_display[value & 0xf]; } int find_hexn(char chh) { register int x; for(x = 0; x < 16;x++) { if(chh == nibble_display[x]) return x; if(chh == (nibble_display[x] + 'a' - 'A')) return x; } return 0; } int hex2nibn(char *str) { register int a; a = find_hexn(str[0]); return a; } int hex2charn(char *str) { register int a = find_hexn(str[0]); register int b = find_hexn(str[1]); return (a << 4) + b; } long hex2longn(char *str) { register int a = hex2charn(str); register int b = hex2charn(str + 2); register int c = hex2charn(str + 4); register int d = hex2charn(str + 6); return (a << 24) + (b << 16) + (c << 8) + d; } /************************************ * ************************************/ void get_string(int y, int x,char *str,int maxlen) { register int a; attron(A_REVERSE); for(a = 0;a < maxlen;a++) { move(y,x + a); addch(' '); } echo(); move(y,x); wgetnstr(stdscr,str,maxlen); noecho(); } /************************************ * ************************************/ void clear_area(int y1,int x1,int y2,int x2) { register int a; for(y1 = y1;y1 <= y2;y1++) for(a = x1;a <= x2;a++) { move(y1,a); addch(' '); } } /************************************ * miwi 27/05/96 ************************************/ void ferr_message(char *mess_str) { register amaxy = maxy - 2; set_colour(COL_FERR_MESSAGE); attron(A_BLINK); move(amaxy,0); addstr(mess_str); addstr(" [hit key]"); attroff(A_BLINK); beep(); update_screen(); getch(); clear_area(amaxy,0,amaxy,79); update_screen(); } /*************************************************************************** **************************************************************************** **************************************************************************** * * Main screen * **************************************************************************** **************************************************************************** ***************************************************************************/ /*************************************************************************** * ***************************************************************************/ void main_editor_dis_bg(void) { switch(edit_mode) { case EM_SAMPLE: sm_display_bg(); sm_display_all(); break; case EM_SEQUENCE: se_display_bg(); se_display_all(); break; case EM_PATTERN: pe_display_bg(); pe_display_all(); break; } update_screen(); } /*************************************************************************** * miwi 27/05/96 ***************************************************************************/ void save_song(int song_type) { if(chdir(song_directory) == -1) ferr_message("Couldn't locate song directory."); else { if(song_type) save_funk_module(song_filename); else save_mod_module(song_filename); if(ferr_val == FERR_OK) { move(maxy - 2,0); addstr(song_filename); addstr(" saved.."); if(!song_type) addstr("(as a .MOD file)"); update_screen(); } else ferr_message(ferr_messages[ferr_val]); } } /*************************************************************************** * miwi 27/05/96 ***************************************************************************/ int quit_confirm(void) { register int amaxy = maxy - 2; set_colour(COL_FERR_MESSAGE); attron(A_BLINK); clear_area(amaxy,0,amaxy,79); move(amaxy,0); addstr("Quit to main menu yes? [y/N]"); attroff(A_BLINK); beep(); update_screen(); ch = getch(); clear_area(amaxy,0,amaxy,79); update_screen(); if((ch == 'y') || (ch == 'Y')) return 1; ch = 0; return 0; } /*************************************************************************** * ***************************************************************************/ void global_keys(void) { switch(ch) { case FC_SCREEN_ESCAPE: if(quit_confirm()) edit_mode = EM_MAIN; break; case FC_TRAC_START: play_song(0); main_editor_dis_bg(); break; case FC_TRAC_PAT: play_song(1); main_editor_dis_bg(); break; case FC_SAVE_SONG: save_song(1); break; case FC_SAVE_MOD: save_song(0); break; } } /*************************************************************************** * ***************************************************************************/ void main_editor_loop(void) { pe_copy_buffer = malloc(64 * MAXIMUM_CHANNELS * sizeof(tslot)); if(pe_copy_buffer != NULL) { edit_mode = EM_SAMPLE; /*sample editor*/ sm_pos_real = 0; sm_pos_hl = 0; sm_pos_hl_old = 0; sm_sd_option = 0; /*sequence editor*/ se_pos_real = 0; se_pos_hl = 0; se_pos_hl_old = 0; /*pattern editor*/ pe_pattern_no = 0; pe_note_no = 0; pe_octave_no = 2; pe_chan_real = 0; pe_pos_real = 0; pe_chan_hl = 0; pe_pos_hl = 0; pe_chan_hl_old = 0; pe_pos_hl_old = 0; pe_nodischans = funk_info.no_active_channels; if(pe_nodischans > 8) pe_nodischans = 8; pe_mchan_begin = 0; pe_mchan_end = 0; pe_mpos_begin = 0; pe_mpos_end = 0; pe_mflag = 0; pe_copied_flag = 0; main_editor_dis_bg(); while(edit_mode != EM_MAIN) { ch = getch(); switch(edit_mode) { case EM_SAMPLE: sm_main(); break; case EM_SEQUENCE: se_main(); break; case EM_PATTERN: pe_main(); break; } } dealloc_funk_mem(); free(pe_copy_buffer); } else ferr_message("Error: Couldn't allocate copy buffer."); } /*************************************************************************** * miwi 27/05/96 ***************************************************************************/ void ms_menu(void) { set_colour(COL_TITLE); hl_bar(menu_option,0); move(3,2); addstr(" NEW SONG "); hl_bar(menu_option,1); move(4,2); addstr(" LOAD SONG "); hl_bar(menu_option,2); move(6,2); addstr("SET SNG DIR"); hl_bar(menu_option,3); move(8,2); addstr(" OPTIMISE "); hl_bar(menu_option,4); move(9,2); addstr(" CHG TRCKS "); hl_bar(menu_option,5); move(10,2); addstr("08bit -> 16"); hl_bar(menu_option,6); move(11,2); addstr("16bit -> 08"); hl_bar(menu_option,7); move(13,2); addstr(" QUIT "); } void ms_display_bg(void) { register int y; register int amaxy = maxy - 5; display_topbar(); for(y = 1;y < (maxy - 5);y++) { move(y,0); addch(' '); move(y,14); addch(' '); move(y,79); addch(' '); } move(y,0); clear_area(amaxy,0,amaxy,79); ms_menu(); } /*************************************************************************** * miwi 27/05/96 ***************************************************************************/ void ms_nfile_display(char option) { char str[3]; hl_bar(option,0); move(3,16); addstr(" Name: "); move(3,27); addstr(filename); move(4,16); hl_bar(option,1); addstr("Precision: "); if(funk_info.sample_precision == 16) addstr("16 bit"); else addstr("08 bit"); addstr(" Module"); hl_bar(option,2); move(5,16); addstr(" Channels: Channels"); move(5,27); sprintf(str,"%d",funk_info.no_active_channels); addstr(str); hl_bar(option,3); move(7,16); addstr("> B A C K <"); hl_bar(option,4); move(8,16); addstr("> O K <"); } void ms_new_file(void) { register int quit_f = 1,option = 0; set_colour(COL_TITLE); funk_info.sample_precision = 8; clear_area(3,16,(maxy - 6),78); strcpy(filename,"new_song"); move((maxy - 8),17); addstr("Use the arrow keys to select attribute"); move((maxy - 7),17); addstr("then enter to change."); ms_nfile_display(option); update_screen(); while(quit_f) { ch = getch(); switch(ch) { case FC_SCREEN_ESCAPE: quit_f = 0; break; case FC_ARROW_UP: if(option > 0) option--; ms_nfile_display(option); update_screen(); break; case FC_ARROW_DN: if(option < (5 - 1)) option++; ms_nfile_display(option); update_screen(); break; case FC_ENTER: switch(option) { case 0: get_string(3,27,filename,SONGNAME_SIZE); ms_nfile_display(option); update_screen(); break; case 1: if(funk_info.sample_precision == 8) funk_info.sample_precision = 16; else funk_info.sample_precision = 8; ms_nfile_display(option); update_screen(); break; case 3: quit_f = 0; break; case 4: if(strlen(filename)) { strcpy(song_filename,filename); strcat(song_filename,FN_EXTENSION); new_funk_module(); if(ferr_val == FERR_OK) { main_editor_loop(); quit_f = 0; ms_display_bg(); } } else ferr_message("You need to give new song a name before proceeding."); break; } break; case FC_ARROW_RIGHT: switch(option) { case 2: if(funk_info.no_active_channels < MAXIMUM_CHANNELS) funk_info.no_active_channels++; ms_nfile_display(option); update_screen(); break; } break; case FC_ARROW_LEFT: switch(option) { case 2: if(funk_info.no_active_channels > 4) funk_info.no_active_channels--; ms_nfile_display(option); update_screen(); break; } break; } } clear_area(3,16,(maxy - 6),78); update_screen(); } /*************************************************************************** **************************************************************************** * * credit scroller * **************************************************************************** ***************************************************************************/ char *credit_scroll[] = { "", "= F u n k t r a c k e r G O L D =", "", "A FREEWARE Digital Module Tracker for Unix", "8/16 bit sampling - N channel mixxing", "", "Copyright (C) 1994,1996,1998 Jason Nunn", "Now under GNU GPL. Read COPYING file.", "", NULL, "", "=Contacts=", "", "32 Rothdale Rd Or 2/119 Nelson Rd ", "Moil NT 0810 Valley View SA 5093 ", "Australia Australia (temporary)", "", "jsno@downunder.net.au", "www.downunder.net.au/~jsno", "", NULL, "", " =GPL= ", "", "FunktrackerGOLD comes under the GNU", "GENERAL PUBLIC LICENSE AGREEMENT,and", "comes with ABSOLUTELY NO WARRANTY.", "This is free software, and you are", "welcome to redistribute it under", "certain conditions. ", "", " Support Free Software! ", "", NULL }; int credit_ptrs[] = {0,10,0,21}; int sc_ptr = 0; int ev_ptr = 0; int ev_count = 0; int scroll_ptr; /************************************* * *************************************/ void print_credit_line(void) { register int y = 0; set_colour(COL_TEXT2); for(;;) { register int y_pos,y_line; y_pos = y + scroll_ptr; y_line = credit_ptrs[sc_ptr] + y; if(credit_scroll[y_line] == NULL) break; if(y_pos < (maxy - 5)) { register int x_pos; move(y_pos,24); addstr(" "); x_pos = 46 - (strlen(credit_scroll[y_line]) >> 1); move(y_pos,x_pos); addstr(credit_scroll[y_line]); } y++; } } /************************************* * *************************************/ int ev_credit_line(int sig) { signal(sig,(void *)&ev_credit_line); switch(ev_ptr) { case 0: ev_count = 0; ev_ptr++; case 1: if(ev_count++ > 10) ev_ptr++; break; case 2: scroll_ptr = (maxy - 5); ev_ptr++; break; case 3: print_credit_line(); if(scroll_ptr == 2) ev_ptr++; else scroll_ptr--; break; case 4: ev_count = 0; ev_ptr++; case 5: if(ev_count++ > 100) ev_ptr++; break; case 6: print_credit_line(); if(scroll_ptr == (maxy - 5)) ev_ptr++; else scroll_ptr++; break; case 7: if(++sc_ptr > 3) sc_ptr = 0; ev_ptr = 0; break; } update_screen(); return 1; } /*************************************************************************** * ***************************************************************************/ void print_ntrac_param(int no_channels) { char tmpstr[6]; sprintf(tmpstr," %d ",no_channels); move(8,44); addstr(tmpstr); } void get_ntrac_param(void) { register int option = 1,new_channels; attron(A_REVERSE); clear_area(6,28,10,49); attroff(A_REVERSE); clear_area(7,29,9,48); move(8,30); addstr("New capacity: "); new_channels = funk_info.no_active_channels; print_ntrac_param(new_channels); while(option) { ch = getch(); switch(ch) { case FC_ENTER: load_funk_module(song_filename); if(ferr_val == FERR_OLD_FK) ferr_val = FERR_OK; if(ferr_val == FERR_OK) { save_ntrac_funk_module(song_filename,new_channels); if(ferr_val != FERR_OK) ferr_message(ferr_messages[ferr_val]); } else ferr_message(ferr_messages[ferr_val]); option = 0; break; case FC_ARROW_UP: if(--new_channels < 4) new_channels = 4; print_ntrac_param(new_channels); break; case FC_ARROW_DN: if(++new_channels > MAXIMUM_CHANNELS) new_channels = MAXIMUM_CHANNELS; print_ntrac_param(new_channels); break; } } } /*************************************************************************** * ***************************************************************************/ void ms_main(void) { struct itimerval oldtimer,timer; register int totype; edit_mode = EM_MAIN; ms_display_bg(); update_screen(); signal(SIGALRM,(void *)&ev_credit_line); timer.it_value.tv_sec = 0; timer.it_interval.tv_sec = 0; timer.it_value.tv_usec = 50000; timer.it_interval.tv_usec = 50000; setitimer(ITIMER_REAL,&timer,&oldtimer); while(edit_mode != EM_QUIT) { ch = getch(); switch(ch) { case FC_SCREEN_ESCAPE: edit_mode = EM_QUIT; break; case FC_ARROW_UP: if(menu_option > 0) menu_option--; ms_menu(); update_screen(); break; case FC_ARROW_DN: if(menu_option < (8 - 1)) menu_option++; ms_menu(); update_screen(); break; case FC_ENTER: setitimer(ITIMER_REAL,&oldtimer,&timer); switch(menu_option) { case 0: /*new song*/ ms_new_file(); break; case 1: /*load song*/ if(get_song_file_name(song_directory,&song_fpos_real,&song_fpos_hl, &song_fpos_hl_old,song_filename) != -1) { if((strstr(song_filename,".Funk") != NULL) || (strstr(song_filename,".fnk") != NULL)) { load_funk_module(song_filename); if(ferr_val == FERR_OK) main_editor_loop(); else if(ferr_val == FERR_OLD_FK) { ferr_message(ferr_messages[ferr_val]); main_editor_loop(); } else ferr_message(ferr_messages[ferr_val]); } else if(strstr(song_filename,".mod") != NULL) { load_mod_module(song_filename); if(ferr_val == FERR_OK) main_editor_loop(); else ferr_message(ferr_messages[ferr_val]); } else ferr_message(ferr_messages[FERR_SNGUNKNOWN]); } ms_display_bg(); break; case 2: /*change song directory*/ get_song_file_name(song_directory,&song_fpos_real,&song_fpos_hl, &song_fpos_hl_old,song_filename); ms_display_bg(); break; case 3: /*optimise module*/ if(get_song_file_name(song_directory,&song_fpos_real,&song_fpos_hl, &song_fpos_hl_old,song_filename) != -1) if((strstr(song_filename,".Funk") != NULL) || (strstr(song_filename,".fnk") != NULL)) { opti_funk_module(song_filename); if(ferr_val != FERR_OK) ferr_message(ferr_messages[ferr_val]); } else ferr_message(ferr_messages[FERR_SNGUNKNOWN]); ms_display_bg(); break; case 4: /*ntrac converter*/ if(get_song_file_name(song_directory,&song_fpos_real,&song_fpos_hl, &song_fpos_hl_old,song_filename) != -1) if((strstr(song_filename,".Funk") != NULL) || (strstr(song_filename,".fnk") != NULL)) get_ntrac_param(); else ferr_message(ferr_messages[FERR_SNGUNKNOWN]); ms_display_bg(); break; case 5: /*8 bit to 16 bit convert*/ totype = 1; goto do_precconv; case 6: /*16 bit to 8 bit convert*/ totype = 0; do_precconv: if(get_song_file_name(song_directory,&song_fpos_real,&song_fpos_hl, &song_fpos_hl_old,song_filename) != -1) if((strstr(song_filename,".Funk") != NULL) || (strstr(song_filename,".fnk") != NULL)) { convprec_funk_module(song_filename,totype); if(ferr_val != FERR_OK) ferr_message(ferr_messages[ferr_val]); } else ferr_message(ferr_messages[FERR_SNGUNKNOWN]); ms_display_bg(); break; case 7: edit_mode = EM_QUIT; break; } print_credit_line(); update_screen(); setitimer(ITIMER_REAL,&timer,&oldtimer); break; } } setitimer(ITIMER_REAL,&oldtimer,&timer); } /*************************************************************************** **************************************************************************** **************************************************************************** * * main stuff * **************************************************************************** **************************************************************************** ***************************************************************************/ char pause_cmdline = 0; int cmdline_sr = DEFAULT_SAM_RATE, cmdline_prec = DEFAULT_PRECISION, cmdline_stereo = DEFAULT_STEREO; /*************************************************************************** * ***************************************************************************/ int cmdline(int argc,char *argv[]) { register int x; int y; for(x = 0;x < argc;x++) { if(argv[x][0] == '-') { switch(argv[x][1]) { case 'h': printf( "\nUsage: funkgold \n" " -h Display this help info\n" " -! Pause on startup\n" " -pxxx Pattern allocation number 0 to 128 (default: %d)\n" " -sxxxxx Sample Rate 6000 to 44100 (default: %d hz)\n" " -uxxxxxx Display (fg) delay rate (default: %d ms)\n" " -Pxx Output Precision (-P8 = 8 bit, -P16 = 16 bit) (default: %d)\n" " -Sx Output Stereo (-S0 = Mono, -S1 = Stereo) (default: %s)\n" "\n", MAXIMUM_PATTERNS, DEFAULT_SAM_RATE, FG_SLEEP, DEFAULT_PRECISION, DEFAULT_STEREO == 1 ? "Stereo" : "Mono" ); return 0; case '!': pause_cmdline = 1; break; case 'p': sscanf(argv[x],"-p%d",&funk_info.funk_pd_size); break; case 's': sscanf(argv[x],"-s%d",&cmdline_sr); break; case 'u': sscanf(argv[x],"-u%d",&fg_sleep); break; case 'P': sscanf(argv[x],"-P%d",&y); if(y == 8) cmdline_prec = 8; if(y == 16) cmdline_prec = 16; break; case 'S': sscanf(argv[x],"-S%d",&cmdline_stereo); break; } } } return 1; } /*************************************************************************** * ***************************************************************************/ int main(int argc,char *argv[]) { printf(WELCOME); funk_info.funk_pd_size = MAXIMUM_PATTERNS; if(cmdline(argc,argv)) { if(funk_info.funk_pd_size < 1) funk_info.funk_pd_size = 1; if(funk_info.funk_pd_size > 128) funk_info.funk_pd_size = 128; get_environment(); printf("Max Pattern allocation: %d\n",funk_info.funk_pd_size); getcwd(song_directory,1024); getcwd(sample_directory,1024); if(open_dsp(cmdline_sr,cmdline_prec,cmdline_stereo)) { printf("Display (fg) delay rate: %d ms\n",fg_sleep); if(pause_cmdline) { printf("Pausing for 10 seconds. Please wait...\n"); sleep(10); } initscr(); maxy = 0; if(LINES >= 24) maxy = 24; if(LINES >= 40) maxy = 40; if(maxy) { start_color(); init_colour_pairs(); cbreak(); noecho(); keypad(stdscr,TRUE); ms_main(); clear(); refresh(); resetterm(); echo(); endwin(); } else { endwin(); printf("Error: Screen size is too small. Program aborted.\n"); } } close_dsp(); } return 1; }