| 1 | /* Ghetto Sound - Sampler |
| 2 | * by: Harvie 2o1o |
| 3 | * |
| 4 | * TODO: |
| 5 | * - port unbuffered getchar to windows |
| 6 | */ |
| 7 | |
| 8 | #include <stdio.h> |
| 9 | #include <stdlib.h> |
| 10 | #include <string.h> |
| 11 | #include <unistd.h> |
| 12 | #include <termio.h> |
| 13 | #include "SDL.h" |
| 14 | #include "SDL_mixer.h" |
| 15 | //Docs: http://www.libsdl.org/projects/SDL_mixer/docs/ |
| 16 | //example http://www.kekkai.org/roger/sdl/mixer/ |
| 17 | |
| 18 | static struct termios stored_settings; |
| 19 | void tc_reset() { tcsetattr(0,TCSANOW,&stored_settings); } |
| 20 | void icanon_off() { |
| 21 | struct termios new_settings; |
| 22 | tcgetattr(0,&stored_settings); |
| 23 | new_settings = stored_settings; |
| 24 | new_settings.c_lflag &= (~ICANON); |
| 25 | new_settings.c_lflag &= (~ECHO); |
| 26 | new_settings.c_cc[VTIME] = 0; |
| 27 | new_settings.c_cc[VMIN] = 1; |
| 28 | tcsetattr(0,TCSANOW,&new_settings); |
| 29 | atexit(tc_reset); |
| 30 | } |
| 31 | |
| 32 | /*char gethit() { |
| 33 | icanon_off(); |
| 34 | char c = getchar(); |
| 35 | tc_reset(); |
| 36 | return c; |
| 37 | }*/ |
| 38 | |
| 39 | Mix_Chunk **sample = NULL; |
| 40 | int samplec = 0; |
| 41 | |
| 42 | void cleanUp() { |
| 43 | int i; |
| 44 | for(i=0;i<samplec;i++) { Mix_FreeChunk(sample[i]); } |
| 45 | Mix_CloseAudio(); |
| 46 | SDL_Quit(); |
| 47 | } |
| 48 | |
| 49 | char *keymap = "xcvbnmasdfghjklwertuiop789456123zy"; |
| 50 | int i; |
| 51 | int main(int argc, char* argv[]) { |
| 52 | if(argc == 1) { |
| 53 | printf("\n\tUsage:\t%s *.wav *.ogg [...]\n", argv[0]); |
| 54 | printf("\t\t%s --keys 7894561230 *.wav *.ogg [...] #to use own key-bindings\n", argv[0]); |
| 55 | puts("\t\t- lowercase: play sample once; uppercase: loop sample;"); |
| 56 | puts(""); |
| 57 | puts("\t- currently supported formats: WAVE, AIFF, RIFF, OGG(libvorbis), VOC, MOD(libmikmod), MIDI, MP3(smpeg)"); |
| 58 | puts("\t- currently there are issues with mp3s - you should convert it:"); |
| 59 | puts("\t\t- mp3 to wav: ffmpeg -i in.mp3 out.wav"); |
| 60 | puts("\t\t- mp3 to ogg: ffmpeg -i in.mp3 -acodec vorbis -ac 2 -aq 60 out.ogg"); |
| 61 | puts(""); |
| 62 | exit(-1); |
| 63 | } |
| 64 | |
| 65 | //Init SDL: |
| 66 | if(SDL_Init(SDL_INIT_AUDIO)) { |
| 67 | perror("Cannot initialize SDL using SDL_Init!\n"); |
| 68 | exit(1); |
| 69 | } |
| 70 | atexit(cleanUp); |
| 71 | //Open audio |
| 72 | if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 256)) { //sampl.freq(def22050), format, 2stereo/1mono, chunksize(default 4095) |
| 73 | perror("Cannot open audio using Mix_OpenAudio!\n"); |
| 74 | exit(2); |
| 75 | } |
| 76 | //alloc enough mixer channels |
| 77 | Mix_AllocateChannels(argc*2); |
| 78 | //set volume |
| 79 | Mix_Volume(-1,MIX_MAX_VOLUME/2); |
| 80 | |
| 81 | //alloc enough samples |
| 82 | sample = malloc(sizeof(Mix_Chunk)*argc); |
| 83 | samplec = 0; |
| 84 | |
| 85 | |
| 86 | i = 1; |
| 87 | argv++; argc--; //get rid of argv[0] |
| 88 | while(argc) { //loop through args (load samples and parse options) |
| 89 | if(!strcmp(argv[0], "--keys")) { //alternative keymap |
| 90 | if(samplec > 0) { |
| 91 | puts("\nERR: Cannot specify keys after loading first sample!"); |
| 92 | break; |
| 93 | } |
| 94 | if(--argc) { argv++; } |
| 95 | else { break; } |
| 96 | keymap = argv[0]; |
| 97 | argv++; argc--; continue; |
| 98 | } |
| 99 | if(samplec < strlen(keymap)) { |
| 100 | sample[samplec] = Mix_LoadWAV(argv[0]); |
| 101 | if(sample[samplec]) { |
| 102 | printf("[%c]: %s%c", keymap[samplec], argv[0], (samplec+1)%4==0?'\n':'\t'); |
| 103 | samplec++; |
| 104 | } else { |
| 105 | printf("\nERR: Mix_LoadWAV(%s): %s\n", argv[0], Mix_GetError()); |
| 106 | } |
| 107 | } else { |
| 108 | if(i) puts("\nERR: Not enough keys on keyboard (or in keymap)!"); |
| 109 | i = 0; |
| 110 | } |
| 111 | |
| 112 | argv++; argc--; |
| 113 | } |
| 114 | puts(""); |
| 115 | |
| 116 | //Main loop: |
| 117 | puts("[Q]: exit!\t[SPACE]: silence!\t[ENTER]: delay\t[-]: halucination"); |
| 118 | int keymaplen = strlen(keymap); |
| 119 | //freopen("test.seq", "r", stdin); |
| 120 | |
| 121 | icanon_off(); |
| 122 | char c; while(c = getchar()) { |
| 123 | //lowercase: play sample once |
| 124 | for(i=0;i<keymaplen && i < samplec;i++) if(c == keymap[i]) { |
| 125 | if(Mix_PlayChannel(samplec+i, sample[i], 0) == -1) printf("ERR: Mix_PlayChannel(sample #%d): %s\n", i, Mix_GetError()); |
| 126 | } |
| 127 | //uppercase: start/stop loop |
| 128 | for(i=0;i<keymaplen && i < samplec;i++) if(c == keymap[i]-'a'+'A') { |
| 129 | if(Mix_Playing(i)) { |
| 130 | Mix_HaltChannel(i); |
| 131 | } else { |
| 132 | if(Mix_PlayChannel(i, sample[i], -1) == -1) printf("ERR: Mix_PlayChannel(sample #%d, loop): %s\n", i, Mix_GetError()); |
| 133 | } |
| 134 | } |
| 135 | if(c == ' ') Mix_HaltChannel(-1); |
| 136 | if(c == '\n') sleep(1); |
| 137 | if(c == '#') while(getchar() != '\n'); |
| 138 | if(c == '?') while(Mix_Playing(-1)) usleep(1000); |
| 139 | if(c == '}') rewind(stdin); |
| 140 | if(c == 'Q' || c == 'q') break; |
| 141 | if(c == '-') for(i=360;i>=0;i--) { //halucination |
| 142 | Mix_SetPosition(MIX_CHANNEL_POST, i, 0); |
| 143 | usleep(2000); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | exit(0); |
| 148 | } |
| 149 | |