int main(int argc, char *argv[]) { int size; int curarg; uint8 *buf; Interface = SexyAL_Init(0); DriverTypes = Interface->EnumerateTypes(Interface); puts("\n*** Festalon v"FESTALON_VERSION); if(argc<=1) { printf("\tUsage: %s [options] file1.nsf file2.nsf ... fileN.nsf\n\n",argv[0]); puts("\tExample Options:"); puts("\t -rate 44100\t\tSet playback rate to 44100Hz(default: 48000Hz)."); puts("\t -quality 1\t\tSet quality to 1, the highest(the lowestis 0; default: 0)."); puts("\t -volume 200\t\tSet volume to 200%(default: 100%)."); puts("\t -buffering 100\t\tSet desired buffering level to 100 milliseconds(default: 100)."); puts("\t -lowpass 1\t\tTurn on lowpass filter(default: 0)."); puts("\t -lowpasscorner 8000\tSet lowpass corner frequency(at which the response is about -3dB) to 8000Hz(default: 10000Hz)."); puts("\t -lowpassorder 2\tSet lowpass filter order to 2(default: 2)."); puts("\t -record filename.wav\tRecords audio in MS PCM format. An existing file won't be overwritten."); puts("\t\t\t\tWhen playing multiple files, only the first opened file will have its music recorded."); puts("\t -ao x\t\t\tSelect output type/driver. Valid choices are:"); { int x = 0; while(DriverTypes[x].name) { printf("\t\t\t\t %s - %s",DriverTypes[x].short_name,DriverTypes[x].name); if(!x) printf("\t(*Default*)"); puts(""); x++; } } puts("\t -aodevice id\t\tSelect output device by id. The default is \"NULL\", which opens the default/preferred device."); puts("\t\t\t\t Try \"-aodevice help\" to see the list of devices."); Interface->Destroy(Interface); return(-1); } curarg = ParseArguments(argc, argv, TODArgs); if(-1 == (CurDriverIndex = FindCurDriver(DriverTypes, Config.ao))) return(-1); if(Config.aodevice) { if(!strcmp(Config.aodevice,"help")) { SexyAL_enumdevice *devices = Interface->EnumerateDevices(Interface, DriverTypes[CurDriverIndex].type); if(!devices) { printf("\tNo predefined output devices are available with this driver type.\n"); return(-1); } printf("\tOutput devices(ID, name) for \"%s\":\n",DriverTypes[CurDriverIndex].name); while(devices->next) { printf("\t %s, %s\n",devices->id,devices->name); devices = devices->next; } return(-1); } } if(Config.quality != 0 && Config.quality != 1) Config.quality = 0; tcgetattr(0,&oldtio); newtio=oldtio; newtio.c_lflag&=~(ICANON|ECHO); signal(SIGTERM,siggo); signal(SIGINT,siggo); tcsetattr(0,TCSANOW,&newtio); sa = fcntl(fileno(stdin), F_GETFL); fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); for(;curarg < argc && !eexit; curarg++) //while(argc-->1 && !eexit) { FILE *fp; printf("\nLoading %s... ",argv[curarg]); if(!(fp=fopen(argv[curarg],"rb"))) {printf("Error opening file: %s\n",strerror(errno));continue;} fseek(fp,0,SEEK_END); size=ftell(fp); fseek(fp,0,SEEK_SET); buf=malloc(size); fread(buf,1,size,fp); fclose(fp); if(!(Player=FESTAI_Load(buf,size) )) { puts("Error loading file!"); free(buf); continue; } free(buf); puts(""); memset(&format, 0, sizeof(format)); memset(&buffering, 0, sizeof(buffering)); format.sampformat = SEXYAL_FMT_PCMFLOAT; format.channels = Player->OutChannels; format.rate = Config.rate; buffering.fragsizems = 10; // Granularity. buffering.ms = Config.buffering; printf(" Using \"%s\" audio driver:",DriverTypes[CurDriverIndex].name); if(!(Output=Interface->Open(Interface,Config.aodevice,&format,&buffering, DriverTypes[CurDriverIndex].type))) { puts("Error opening sound device."); continue; //return(-1); } putchar('\n'); printf(" Playback Rate:\t%dHz\n",format.rate); printf(" Output Channels:\t%d\n",format.channels); printf(" Output Format:\t"); if(format.sampformat == SEXYAL_FMT_PCMFLOAT) puts("\t32-bit Floating Point"); else { if(format.sampformat&0x1) printf("Signed, "); else printf("Unsigned, "); printf("%d bits\n",(format.sampformat >> 4) << 3); } printf(" Estimated Latency:\t%d ms\n",buffering.latency * 1000 / format.rate); FESTAI_SetSound(Player, format.rate, Config.quality); if(WRFilename) { if(!FCEUI_BeginWaveRecord(format.rate,Player->OutChannels,WRFilename)) { free(WRFilename); WRFilename = 0; } } //printf("%d:%d\n",buffering.fragsize, buffering.fragcount); format.sampformat=SEXYAL_FMT_PCMFLOAT; format.channels=Player->OutChannels; format.byteorder=0; Output->SetConvert(Output,&format); Config.volume = FESTAI_SetVolume(Player, Config.volume); FESTAI_Disable(Player, disabled); poosh(); puts(""); ShowInfo(); if(!FESTAI_SetLowpass(Player, Config.lowpass, Config.lowpasscorner, Config.lowpassorder)) { puts(" Error activating lowpass filter!"); } else if(Config.lowpass) printf(" Lowpass filter on. Corner frequency: %dHz, Order: %d\n",Config.lowpasscorner,Config.lowpassorder); current=FESTAI_SongControl(Player,0,0); lastms = ~0; frames_written = 0; puts("\n\n"); ShowStatus(1,1); //#define TICKEROO #ifdef TICKEROO static uint64 last_tick,total_ticks,total; total = 0; total_ticks = 0; #endif while(!eexit) { static char t[3]={0,0,0}; int len; float *buf; ShowStatus(0,0); if(paused) { usleep(10); } else { #ifdef TICKEROO last_tick=getticks(); #endif buf=FESTAI_Emulate(Player, &len); #ifdef TICKEROO total_ticks += getticks() - last_tick; total += len; #endif if(WRFilename) FCEU_WriteWaveData(buf, len); frames_written += len; Output->Write(Output,buf,len); #ifdef TICKEROO if(total >= 100000/2) { printf("%8f\n", (double)total*1000000/total_ticks); rmode(); exit(1); } #endif } while(read(fileno(stdin),&t[0],1)>=0) { static char kcc[20] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')'}; int x; for(x=0; x<20; x++) if(kcc[x] == t[0]) { disabled^=1<<x; FESTAI_Disable(Player,disabled); poosh(); ShowStatus(0,1); break; } if(t[2]==27 && t[1]==91) switch(t[0]) { case 65:current=FESTAI_SongControl(Player,10,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; case 66:current=FESTAI_SongControl(Player,-10,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; case 67:current=FESTAI_SongControl(Player,1,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; case 68:current=FESTAI_SongControl(Player,-1,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; } else switch(tolower(t[0])) { case 10: lastms = ~0;frames_written=0;poosh();current=FESTAI_SongControl(Player, 0,0);ShowStatus(1,1);break; case '`': poosh();break; case 'a': Config.volume-=25; Config.volume = FESTAI_SetVolume(Player, Config.volume);poosh();ShowStatus(0,1);break; case 's': Config.volume+=25; Config.volume = FESTAI_SetVolume(Player, Config.volume);poosh();ShowStatus(0,1);break; case '_': case '-': Config.volume--; FESTAI_SetVolume(Player,Config.volume);poosh();ShowStatus(0,1);break; case '+': case '=': Config.volume++; FESTAI_SetVolume(Player,Config.volume);poosh();ShowStatus(0,1);break; case 'p': TogglePause();break; case 'q': goto exito; /* Alternate song selection keys. Especially needed for DOS port. */ case '\'':current=FESTAI_SongControl(Player,10,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; case ';':current=FESTAI_SongControl(Player,-10,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; case '.':current=FESTAI_SongControl(Player,1,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; case ',':current=FESTAI_SongControl(Player,-1,0);poosh(); lastms = ~0;frames_written=0;ShowStatus(1,1);break; } t[2]=t[1]; t[1]=t[0]; } } exito: if(WRFilename) { FCEUI_EndWaveRecord(); free(WRFilename); WRFilename = 0; } poosh(); FESTAI_Close(Player); if(Output) Output->Close(Output); Output = 0; } rmode(); free(DriverTypes); if(Interface) Interface->Destroy(Interface); return(0); }
static bool RunSexyALTest(SexyAL *interface, SexyAL_buffering *buffering, const char *device, int driver_type) { static const int to_format[9] = { SEXYAL_FMT_PCMU8, SEXYAL_FMT_PCMS8, SEXYAL_FMT_PCMU16, SEXYAL_FMT_PCMS16, SEXYAL_FMT_PCMU24, SEXYAL_FMT_PCMS24, SEXYAL_FMT_PCMU32, SEXYAL_FMT_PCMS32, SEXYAL_FMT_PCMFLOAT }; static const char *to_format_name[9] = { "8-bit unsigned", "8-bit signed", "16-bit unsigned", "16-bit signed", "24-bit unsigned", "24-bit signed", "32-bit unsigned", "32-bit signed", "float" }; // TODO: byte order format conversion. // TODO: source format. const int rate = 48000; const int numframes = (rate / 2 + 1) &~ 1; for(int src_channels = 1; src_channels <= 2; src_channels++) { for(int dest_channels = 1; dest_channels <= 2; dest_channels++) { //for(int src_format = 0; src_format < 9; src_format++) int src_format = 3; { for(int dest_format = 0; dest_format < 9; dest_format++) { printf("Source Format: %s, Source Channels: %d --- Dest Format: %s, Dest Channels: %d\n\n", to_format_name[src_format], src_channels, to_format_name[dest_format], dest_channels); memset(&format,0,sizeof(format)); Interface = (SexyAL *)SexyAL_Init(0); DriverTypes = Interface->EnumerateTypes(Interface); format.sampformat = to_format[dest_format]; format.channels = dest_channels; format.revbyteorder = 0; format.rate = rate; buffering->buffer_size = 0; buffering->period_size = 0; buffering->latency = 0; buffering->bt_gran = 0; if(!(Output=Interface->Open(Interface, device, &format, buffering, driver_type))) { MDFND_PrintError(_("Error opening a sound device.")); Interface->Destroy(Interface); Interface=0; return(0); } if(format.sampformat != to_format[dest_format]) printf("Warning: Could not set desired device format.\n"); if(format.channels != dest_channels) printf("Warning: Could not set desired device channel count.\n"); if(format.rate != rate) printf("Warning: Could not set desired device rate.\n"); format.sampformat = to_format[src_format]; format.channels = src_channels; format.revbyteorder = 0; format.rate = rate; Output->SetConvert(Output, &format); if(to_format[src_format] == SEXYAL_FMT_PCMS16) { int16 samples[numframes * src_channels]; for(int i = 0; i < numframes; i++) { int16 sval = 4095 * sin((double)i * 440 * M_PI * 2 / rate); for(int ch = 0; ch < src_channels; ch++) samples[i * src_channels + ch] = sval; } // Write half in one go, the rest in small chunks. if(!Output->Write(Output, samples, numframes / 2)) printf("Write count error 0\n"); for(int i = numframes / 2; i < numframes; i += 100) { int32 towrite = numframes - i; if(towrite > 100) towrite = 100; if(!Output->Write(Output, samples + i * src_channels, towrite)) printf("Write count error 1\n"); } } Output->Close(Output); Interface->Destroy(Interface); sleep(1); } } } } }
bool InitSound(MDFNGI *gi) { SoundRate = 0; memset(&format,0,sizeof(format)); memset(&buffering,0,sizeof(buffering)); Interface = (SexyAL *)SexyAL_Init(0); DriverTypes = Interface->EnumerateTypes(Interface); format.sampformat = SEXYAL_FMT_PCMS16; assert(gi->soundchan); format.channels = gi->soundchan; format.revbyteorder = 0; format.rate = gi->soundrate ? gi->soundrate : MDFN_GetSettingUI("sound.rate"); buffering.ms = MDFN_GetSettingUI("sound.buffer_time"); buffering.period_us = MDFN_GetSettingUI("sound.period_time"); std::string zedevice = MDFN_GetSettingS("sound.device"); std::string zedriver = MDFN_GetSettingS("sound.driver"); CurDriverIndex = -1; if(!strcasecmp(zedriver.c_str(), "default")) CurDriverIndex = 0; else { for(int x = 0; DriverTypes[x].short_name; x++) { if(!strcasecmp(zedriver.c_str(), DriverTypes[x].short_name)) { CurDriverIndex = x; break; } } } MDFNI_printf(_("\nInitializing sound...\n")); MDFN_indent(1); if(CurDriverIndex == -1) { MDFN_printf(_("\nUnknown sound driver \"%s\". Supported sound drivers:\n"), zedriver.c_str()); MDFN_indent(2); for(int x = 0; DriverTypes[x].short_name; x++) { MDFN_printf("%s\n", DriverTypes[x].short_name); } MDFN_indent(-2); MDFN_printf("\n"); Interface->Destroy(Interface); Interface = NULL; MDFN_indent(-1); return(FALSE); } MDFNI_printf(_("Using \"%s\" audio driver with device \"%s\":"),DriverTypes[CurDriverIndex].name, zedevice.c_str()); MDFN_indent(1); //RunSexyALTest(Interface, &buffering, zedevice.c_str(), DriverTypes[CurDriverIndex].type); //exit(1); if(!(Output=Interface->Open(Interface, zedevice.c_str(), &format, &buffering, DriverTypes[CurDriverIndex].type))) { MDFND_PrintError(_("Error opening a sound device.")); Interface->Destroy(Interface); Interface=0; MDFN_indent(-2); return(FALSE); } if(format.rate<8192 || format.rate > 48000) { MDFND_PrintError(_("Set rate is out of range [8192-48000]")); KillSound(); MDFN_indent(-2); return(FALSE); } MDFNI_printf(_("\nBits: %u\nRate: %u\nChannels: %u\nByte order: CPU %s\nBuffer size: %u sample frames(%f ms)\n"), (format.sampformat>>4)*8,format.rate,format.channels,format.revbyteorder?"Reversed":"Native", buffering.buffer_size, (double)buffering.buffer_size * 1000 / format.rate); MDFNI_printf(_("Latency: %u sample frames(%f ms)\n"), buffering.latency, (double)buffering.latency * 1000 / format.rate); if(buffering.period_size) { int64_t pt_test_result = ((int64_t)buffering.period_size * (1000 * 1000) / format.rate); MDFNI_printf(_("Period size: %u sample frames(%f ms)\n"), buffering.period_size, (double)buffering.period_size * 1000 / format.rate); if(pt_test_result > 5333) { MDFN_indent(1); MDFN_printf(_("Warning: Period time is too large(it should be <= ~5.333ms). Video will appear very jerky.\n")); MDFN_indent(-1); } } format.sampformat=SEXYAL_FMT_PCMS16; format.channels=gi->soundchan?gi->soundchan:1; format.revbyteorder=0; //format.rate=gi->soundrate?gi->soundrate:soundrate; Output->SetConvert(Output, &format); EmuModBufferSize = (500 * format.rate + 999) / 1000; EmuModBuffer = (int16 *)calloc(sizeof(int16) * format.channels, EmuModBufferSize); SoundRate = format.rate; MDFN_indent(-2); return(1); }