#include #include #include #include #include #include #include #define D_SAMPLE_CHANNELS 2 #define D_SAMPLE_RATE 22050 #define D_SAMPLE_BITS 16 #define D_BEATS_PER_MIN 125 #define D_BEATS_PER_TRK 64 #define D_REPEAT_CNT 6 #define D_CHUNK_SIZE (D_BEATS_PER_TRK * D_REPEAT_CNT * D_BEATS_PER_MIN / 60) #define D_FREQ_MASTER (D_SAMPLE_RATE * D_CHUNK_SIZE / 60) #define D_TRK_IDX_SIZE 128 #define D_MAX_AMP 1024 #define D_MAX_INST 4 #define ami2pc(w) do { w = ((((w & 255)*256) | (w / 256)) * 2); } while (0) typedef unsigned int u32; typedef unsigned short u16; typedef unsigned char u8; #pragma pack(1) struct instr_header { char instr_name[22]; u16 instr_length; u8 instr_freq_adj; u8 instr_amplitude; u16 instr_loop_start; u16 instr_loop_count; } __attribute__((__packed__)); struct mod15_header { char module_name[20]; struct instr_header instrs[15]; u8 track_count; u8 module_speed; u8 track_index[D_TRK_IDX_SIZE]; } __attribute__((__packed__)); struct mod31_header { char module_name[20]; struct instr_header instrs[31]; u8 track_count; u8 module_speed; u8 track_index[D_TRK_IDX_SIZE]; char mod31_type[4]; } __attribute__((__packed__)); union mod_header { struct mod15_header m15; struct mod31_header m31; }; struct note { u16 freq_h:4; u16 inst_h:4; u16 freq_l:8; u16 function:4; u16 inst_l:4; u16 parameter:8; }; struct voice_ctrl { u8 *data; u32 instr_data_ofs; u32 instr_data_end; u32 instr_loop_start; u32 instr_loop_count; u32 instr_data_count; u32 instr_data_skip; void (*ctrl_function) (struct voice_ctrl *); u8 vibrato_depth; u8 vibrato_count; u8 vibrato_step; u8 tremolo_depth; u8 tremolo_count; u8 tremolo_step; int frequency_now; int frequency_new; int vibrato_freq_change; int portamento_step; int portamento_freq_now; int frequency; u16 amplitude; u8 fine_tune; u8 instrument; u8 function; u8 parameter; }; int freq_tab [] = { 5136, 4848, 4572, 4304, 4068, 3840, 3624, 3420, 3228, 3048, 2880, 2716, 2568, 2424, 2286, 2160, 2034, 1920, 1812, 1710, 1614, 1524, 1440, 1358, 1284, 1212, 1143, 1080, 1017, 960, 906, 855, 807, 762, 720, 679, 642, 606, 571, 540, 508, 480, 453, 427, 403, 381, 360, 339, 321, 303, 285, 270, 255, 240, 226, 214, 202, 190, 180, 169, 160, 151, 143, 135, 127, 120, 113, 107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56, 53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28, 26, 25, 23, 22, 21, 20, 18, 17, 16, 15, 15, 14, 13, 12, 11, 11, 10, 10, 9, 8, 8, 7, 7, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, }; int sinus_tab [64] = { 0, 6, 12, 18, 24, 30, 36, 40, 45, 49, 53, 56, 59, 61, 62, 63, 63, 62, 61, 59, 56, 53, 49, 45, 40, 36, 30, 24, 18, 12, 6, 0, -0, -6, -12, -18, -24, -30, -36, -40, -45, -49, -53, -56, -59, -61, -62, -63, -63, -62, -61, -59, -56, -53, -49, -45, -40, -36, -30, -24, -18, -12, -6, 0, }; u8 dummy [128]; #define DMA_SIZE 32768 #define DMA_HALF (DMA_SIZE/2) #define DMA_WRAP (DMA_SIZE-1) short dma_buf16[DMA_SIZE]; char dma_buf08 [DMA_SIZE]; u16 dma_head = 0; u16 dma_tail = 0; u16 dma_count = 0; short amp_table[D_MAX_AMP / 16][256]; char dsp_device_name[128] = "/dev/dsp"; int dsp_device; FILE *modfile; u8 *instr[31]; struct note tracks[256][D_BEATS_PER_TRK][D_MAX_INST]; int verbose = 0; int print_tracks = 0; int play_intro = 0; int bpm_fix = 0; int skp_fix = 0; int stereo_wide = 1; int sample_channels = D_SAMPLE_CHANNELS; int sample_rate = D_SAMPLE_RATE; int sample_bits = D_SAMPLE_BITS; int chunk_size = D_CHUNK_SIZE; int freq_master = D_FREQ_MASTER; int track_count = 0; int module_speed = D_BEATS_PER_MIN; int tot_cnt = 0; u8 *track_index; union mod_header h; struct voice_ctrl voice[D_MAX_INST]; char rep_add = 0; char rep_cnt = 6; char rep_max = 6; u8 trk_ply = 0; u8 track_skip = 0; u8 track_new = 0; u8 trk_ofs = 0; u8 trk_idx = 0; u8 mod_don = 0; char note_n [16]; char inst_n [16]; char func_n [16]; char *note_name(int freq) { int i = 0; if (freq) { while ((i < sizeof(freq_tab) / sizeof(int)) && (freq < freq_tab[i])) i++; if (freq == freq_tab[i]) note_n[0] = ' '; else note_n[0] = '~'; switch (i % 12) { case 0: sprintf(¬e_n[1], "c%d ", i / 12); break; case 1: sprintf(¬e_n[1], "c#%d", i / 12); break; case 2: sprintf(¬e_n[1], "d%d ", i / 12); break; case 3: sprintf(¬e_n[1], "d#%d", i / 12); break; case 4: sprintf(¬e_n[1], "e%d ", i / 12); break; case 5: sprintf(¬e_n[1], "f%d ", i / 12); break; case 6: sprintf(¬e_n[1], "f#%d", i / 12); break; case 7: sprintf(¬e_n[1], "g%d ", i / 12); break; case 8: sprintf(¬e_n[1], "g#%d", i / 12); break; case 9: sprintf(¬e_n[1], "a%d ", i / 12); break; case 10: sprintf(¬e_n[1], "a#%d", i / 12); break; case 11: sprintf(¬e_n[1], "b%d ", i / 12); break; } } else { strcpy(note_n, " "); } return note_n; } char *instrument_name(u8 inst) { if (inst) { sprintf(inst_n, "%2d", inst); } else { strcpy(inst_n, " "); } return inst_n; } char *function_name(u8 function, u8 param) { switch (function) { case 0:if (param) sprintf(func_n, "ARP %X %X", param / 16, param & 15); else sprintf(func_n, " "); break; case 1: sprintf(func_n, "PUP %02X ", param); break; case 2: sprintf(func_n, "PDN %02X ", param); break; case 3: sprintf(func_n, "PTO %02X ", param); break; case 4: sprintf(func_n, "VIB %X %X", param / 16, param & 15); break; case 5: sprintf(func_n, "PDC %02X ", param); break; case 6: sprintf(func_n, "VDC %02X ", param); break; case 7: sprintf(func_n, "TRE %X %X", param / 16, param & 15); break; case 8: sprintf(func_n, "8?? %02X ", param); break; case 9: sprintf(func_n, "OFS %02X ", param); break; case 10: sprintf(func_n, "DCY %X %X", param / 16, param & 15); break; case 11: sprintf(func_n, "TRK %02X ", param); break; case 12: sprintf(func_n, "AMP %02X ", param); break; case 13: sprintf(func_n, "BRK "); break; case 14: switch (param / 16) { case 0: sprintf(func_n, "E0? %X ", param & 15); break; case 1: sprintf(func_n, "FPU %X ", param & 15); break; case 2: sprintf(func_n, "FPD %X ", param & 15); break; case 3: sprintf(func_n, "GLC %X ", param & 15); break; case 4: sprintf(func_n, "VIC %X ", param & 15); break; case 5: sprintf(func_n, "FIN %X ", param & 15); break; case 6: sprintf(func_n, "LTR %02X ", (param & 15) * 4); break; case 7: sprintf(func_n, "TRC %X ", param & 15); break; case 8: sprintf(func_n, "E8? "); break; case 9: sprintf(func_n, "RET "); break; case 10: sprintf(func_n, "FVU %X ", param & 15); break; case 11: sprintf(func_n, "FVD %X ", param & 15); break; case 12: sprintf(func_n, "CUT "); break; case 13: sprintf(func_n, "DLY %X ", param & 15); break; case 14: sprintf(func_n, "DPA %X ", param & 15); break; case 15: sprintf(func_n, "EF? %X ", param & 15); break; } break; case 15: sprintf(func_n, "REP %02X ", param); break; } return func_n; } char *basename(char *name) { char *last_slash; if ((last_slash = strrchr(name, '/'))) return ++last_slash; return name; } int open_module(char *filename) { if ((modfile = fopen(filename, "rb"))) { if (fread(&h, 1, sizeof(h), modfile) == sizeof(h)) { if (verbose) printf("\n%-12s:\"%-20.20s\" ", basename(filename), h.m31.module_name); if ((memcmp(h.m31.mod31_type, "M.K.", 4) == 0) || (memcmp(h.m31.mod31_type, "FLT4", 4) == 0) || (memcmp(h.m31.mod31_type, "FLT8", 4) == 0)) { track_index = h.m31.track_index; track_count = h.m31.track_count; module_speed = h.m31.module_speed; if (module_speed < 20) module_speed = D_BEATS_PER_MIN; if (bpm_fix) module_speed = bpm_fix; if (verbose) printf(" 31 %4.4s\n", h.m31.mod31_type); return 31; } else { fseek(modfile, sizeof(struct mod15_header), SEEK_SET); track_index = h.m15.track_index; track_count = h.m15.track_count; module_speed = h.m15.module_speed; if (module_speed < 20) module_speed = D_BEATS_PER_MIN; if (bpm_fix) module_speed = bpm_fix; if (verbose) printf(" 15\n"); return 15; } } } return 0; } void dump_instr(int i, struct instr_header *s) { printf("%02d: %-20.20s %04X %04X %04X %02d %03d\n", i, s->instr_name, s->instr_length, s->instr_loop_start, s->instr_loop_count, s->instr_amplitude, s->instr_freq_adj ); } int read_module(char *filename) { #define spl(n) h.m31.instrs[n] char sname[23]; int inst_cnt; int i, track_max; int old_fpos; if ((inst_cnt = open_module(filename))) { track_max = 0; for (i = 0; i < 128; i++) { if (track_index[i] > track_max) { track_max = track_index[i]; } if (track_index[i] && i > track_count) track_count = i; } old_fpos = ftell(modfile); for (i = 0; i <= track_max; i++) { if (fread(&tracks[i], 1, 0x400, modfile) != 0x400) return 0; } if (verbose) { tot_cnt = 0; rep_cnt = 6; trk_idx = 0; mod_don = 0; do { trk_ply = track_index[trk_idx]; track_new = 0; trk_ofs = 0; do { for (i = 0; i < 4; i++) { if (tracks[trk_ply][trk_ofs][i].function == 15) { if ((tracks[trk_ply][trk_ofs][i].parameter) < 32) rep_cnt = tracks[trk_ply][trk_ofs][i].parameter; if (!rep_cnt) ++rep_cnt; } if (tracks[trk_ply][trk_ofs][i].function == 11) { trk_idx = tracks[trk_ply][trk_ofs][i].parameter; tracks[trk_ply][trk_ofs][i].function = 0; track_new = 1; } if (tracks[trk_ply][trk_ofs][i].function == 13) { track_new = 1; } } if (!track_new) tot_cnt += rep_cnt + 1; } while ((!track_new) && (++trk_ofs < 64)); if (++trk_idx >= track_count) mod_don = 1; } while (!mod_don); fseek(modfile, old_fpos, SEEK_SET); fread(tracks, 0x400, track_max + 1, modfile); printf("%d tracks [%d different], %d note chunks\n", track_count, track_max + 1, tot_cnt); } for (i = 0; i < inst_cnt; i++) { if (spl(i).instr_length) { if (print_tracks) sprintf(sname, "%-22.22s", spl(i).instr_name); ami2pc(spl(i).instr_length); ami2pc(spl(i).instr_loop_start); ami2pc(spl(i).instr_loop_count); instr[i] = (u8 *) malloc(spl(i).instr_length); if (fread(instr[i], 1, spl(i).instr_length, modfile) != spl(i).instr_length) return 0; if (print_tracks) dump_instr(i, &spl(i)); } } fclose(modfile); return 1; } return 0; } int arpeggio_freq(int f, u8 d) { int i; for (i = 0; i < sizeof(freq_tab) / sizeof(int); i++) { if (f >= freq_tab[i]) return freq_tab[i + d]; } return f; } void ctrl_nothing(struct voice_ctrl *v) { v->ctrl_function = ctrl_nothing; } void ctrl_arpeggio(struct voice_ctrl *v) { if (v->parameter) { switch (rep_cnt % 3) { case 0:break; case 1: v->frequency_now = arpeggio_freq(v->frequency_new, v->parameter & 15); break; case 2: v->frequency_now = arpeggio_freq(v->frequency_new, v->parameter / 16); break; } } else { v->ctrl_function = ctrl_nothing; } } void ctrl_portamento_up(struct voice_ctrl *v) { if (v->frequency_now - v->parameter > 1) { v->frequency_now -= v->parameter; } else { v->frequency_now = 1; } } void ctrl_portamento_dn(struct voice_ctrl *v) { if (v->frequency_now + v->parameter <= 4095) { v->frequency_now += v->parameter; } else { v->frequency_now = 4095; } } void ctrl_portamento_execute(struct voice_ctrl *v) { if (v->portamento_freq_now > v->frequency_new) { v->portamento_freq_now -= v->portamento_step; if (v->portamento_freq_now < v->frequency_new) { v->portamento_freq_now = v->frequency_new; } } else { v->portamento_freq_now += v->portamento_step; if (v->portamento_freq_now > v->frequency_new) { v->portamento_freq_now = v->frequency_new; } } v->frequency_now = v->portamento_freq_now; } void ctrl_portamento_note(struct voice_ctrl *v) { if (v->parameter) { v->portamento_step = v->parameter; } v->ctrl_function = ctrl_portamento_execute; v->ctrl_function(v); } void ctrl_vibrato_execute(struct voice_ctrl *v) { v->vibrato_freq_change = sinus_tab[v->vibrato_count] * v->vibrato_depth / 32; v->vibrato_count = (v->vibrato_count + v->vibrato_step) & 63; } void ctrl_vibrato(struct voice_ctrl *v) { if (v->parameter & 15) { v->vibrato_depth = v->parameter & 15; } if (v->parameter / 16) { v->vibrato_step = v->parameter / 16; } ctrl_vibrato_execute(v); } void ctrl_tremolo(struct voice_ctrl *v) { if (v->parameter & 15) { v->tremolo_depth = v->parameter & 15; } if (v->parameter / 16) { v->tremolo_step = v->parameter / 16; } v->amplitude = D_MAX_AMP / 2 + sinus_tab[v->tremolo_count] * v->tremolo_depth / 2; v->tremolo_count = (v->tremolo_count + v->tremolo_step) & 63; } void ctrl_instrument_offset(struct voice_ctrl *v) { v->instr_data_ofs = 65536L * (int)v->parameter; v->instr_data_count = v->instr_data_end - v->instr_data_ofs; v->ctrl_function = ctrl_nothing; } void ctrl_attack_decay(struct voice_ctrl *v) { if (v->amplitude + (v->parameter / 16) * 4 < D_MAX_AMP) { v->amplitude += (v->parameter / 16) * 4; } else { v->amplitude = D_MAX_AMP; } if (v->amplitude - (v->parameter & 15) * 4 > 0) { v->amplitude -= (v->parameter & 15) * 4; } else { v->amplitude = 0; } } void ctrl_decay(struct voice_ctrl *v) { if (v->amplitude - (v->parameter & 15) * 4 > 0) { v->amplitude -= (v->parameter & 15) * 4; } else { v->amplitude = 0; } } void ctrl_portamento_and_decay(struct voice_ctrl *v) { ctrl_portamento_execute(v); ctrl_decay(v); } void ctrl_vibrato_and_decay(struct voice_ctrl *v) { ctrl_vibrato_execute(v); ctrl_decay(v); } void ctrl_break(struct voice_ctrl *v) { track_new = 1; v->ctrl_function = ctrl_nothing; } void ctrl_new_track(struct voice_ctrl *v) { track_new = 1; trk_idx = v->parameter; v->ctrl_function = ctrl_nothing; } void ctrl_new_amplitude(struct voice_ctrl *v) { if ((int)v->parameter * 16 < D_MAX_AMP) { v->amplitude = v->parameter * 16; } else { v->amplitude = D_MAX_AMP; } } void ctrl_new_repeat_max(struct voice_ctrl *v) { if (v->parameter < 16) { rep_max = v->parameter; if (!rep_max) ++rep_max; rep_cnt = rep_max; } else { module_speed = v->parameter; chunk_size = sample_rate * 60 / module_speed / 30; } v->ctrl_function = ctrl_nothing; } void ctrl_fine_portamento_up(struct voice_ctrl *v) { v->frequency_now -= v->parameter & 15; } void ctrl_fine_portamento_dn(struct voice_ctrl *v) { v->frequency_now += v->parameter & 15; } void ctrl_glissando_control(struct voice_ctrl *v) { } void ctrl_vibrato_control(struct voice_ctrl *v) { v->vibrato_depth = v->parameter & 15; } void ctrl_fine_tune(struct voice_ctrl *v) { v->fine_tune = v->parameter & 15; } void ctrl_loop_inside_track(struct voice_ctrl *v) { trk_ofs = (v->parameter & 15) * 4; } void ctrl_tremolo_control(struct voice_ctrl *v) { v->tremolo_depth = v->parameter & 15; } void ctrl_retrigger_note(struct voice_ctrl *v) { v->instr_data_ofs = 0; v->instr_data_count = v->instr_data_end; v->ctrl_function = ctrl_nothing; } void ctrl_fine_volume_up(struct voice_ctrl *v) { if (v->amplitude + (v->parameter & 15) < D_MAX_AMP) { v->amplitude += v->parameter & 15; } else { v->amplitude = D_MAX_AMP; } } void ctrl_fine_volume_dn(struct voice_ctrl *v) { if (v->amplitude - (v->parameter & 15) > 0) { v->amplitude -= v->parameter & 15; } else { v->amplitude = 0; } } void ctrl_cut_off_note(struct voice_ctrl *v) { v->instr_data_count = 0; v->ctrl_function = ctrl_nothing; } void ctrl_delay_note(struct voice_ctrl *v) { v->instr_data_count += 256 * (int)(v->parameter & 15); v->ctrl_function = ctrl_nothing; } void ctrl_delay_pattern(struct voice_ctrl *v) { rep_add = v->parameter & 15; v->ctrl_function = ctrl_nothing; } void (*ctrl_table_14[16]) (struct voice_ctrl *)= { ctrl_nothing, ctrl_fine_portamento_up, ctrl_fine_portamento_dn, ctrl_glissando_control, ctrl_vibrato_control, ctrl_fine_tune, ctrl_loop_inside_track, ctrl_tremolo_control, ctrl_nothing, ctrl_retrigger_note, ctrl_fine_volume_up, ctrl_fine_volume_dn, ctrl_cut_off_note, ctrl_delay_note, ctrl_delay_pattern, ctrl_nothing, }; void ctrl_14_dispatcher(struct voice_ctrl *v) { v->ctrl_function = ctrl_table_14[v->parameter / 16]; v->parameter &= 15; v->ctrl_function(v); } void (*ctrl_table[16]) (struct voice_ctrl *)= { ctrl_arpeggio, ctrl_portamento_up, ctrl_portamento_dn, ctrl_portamento_note, ctrl_vibrato, ctrl_portamento_and_decay, ctrl_vibrato_and_decay, ctrl_tremolo, ctrl_nothing, ctrl_instrument_offset, ctrl_attack_decay, ctrl_new_track, ctrl_new_amplitude, ctrl_break, ctrl_14_dispatcher, ctrl_new_repeat_max, }; void play_new_note(void) { struct note *n; int i; int f; n = &tracks[trk_ply][trk_ofs][0]; if (print_tracks) printf("\r%03d#%02d: ", trk_ply, trk_ofs); for (i = 0; i < D_MAX_INST; i++) { voice[i].frequency = (n->freq_l + n->freq_h * 256) * 3 / 2; voice[i].parameter = n->parameter; voice[i].instrument = n->inst_l + (n->inst_h * 16); voice[i].function = n->function; if (n->function == 11) { n->function = 0; n->parameter = 0; } voice[i].ctrl_function = ctrl_table[voice[i].function]; if ((voice[i].instrument > 0) && (voice[i].instrument < 32) && (instr[voice[i].instrument - 1])) { voice[i].data = instr[voice[i].instrument - 1]; voice[i].instr_data_ofs = 0; voice[i].amplitude = 16 * h.m31.instrs[voice[i].instrument - 1].instr_amplitude; voice[i].instr_data_end = 256 * (int)h.m31.instrs[voice[i].instrument - 1].instr_length; voice[i].instr_loop_start = 256 * (int)h.m31.instrs[voice[i].instrument - 1].instr_loop_start; voice[i].instr_loop_count = 256 * (int)h.m31.instrs[voice[i].instrument - 1].instr_loop_count; voice[i].instr_data_count = voice[i].instr_data_end; } if (voice[i].frequency) { voice[i].frequency_new = voice[i].frequency; voice[i].portamento_freq_now = voice[i].frequency_now; voice[i].frequency_now = voice[i].frequency_new; voice[i].instr_data_count = voice[i].instr_data_end; voice[i].vibrato_count = 0; voice[i].tremolo_count = 0; voice[i].instr_data_ofs = 0; } voice[i].vibrato_freq_change = 0; voice[i].ctrl_function(&voice[i]); if (print_tracks) printf("%s %s %s: ", note_name(voice[i].frequency), instrument_name(voice[i].instrument), function_name(voice[i].function, voice[i].parameter)); f = (voice[i].frequency_now + voice[i].vibrato_freq_change); if (f) { voice[i].instr_data_skip = freq_master / f; } else { voice[i].instr_data_skip = 0; } n++; /* next note */ } if (print_tracks) printf("\n"); if (++trk_ofs == D_BEATS_PER_TRK) track_new = 1; } void play_chg_note(void) { int i; int f; for (i = 0; i < D_MAX_INST; i++) { voice[i].ctrl_function(&voice[i]); f = (voice[i].frequency_now + voice[i].vibrato_freq_change); if (f) { voice[i].instr_data_skip = freq_master / f; } else { voice[i].instr_data_skip = 0; } } } void fill_dma_08_1(void) { u16 dma_cnt; int i; dma_cnt = chunk_size; do { dma_buf08[dma_head] = 0x80 + (amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))] + amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))] + amp_table[voice[2].amplitude / 16][*(voice[2].data + (voice[2].instr_data_ofs / 256))] + amp_table[voice[3].amplitude / 16][*(voice[3].data + (voice[3].instr_data_ofs / 256))]) / 256; dma_head = (dma_head + 1) & DMA_WRAP; for (i = 0; i < D_MAX_INST; i++) { voice[i].instr_data_ofs += voice[i].instr_data_skip; if (voice[i].instr_data_ofs >= voice[i].instr_data_end) { voice[i].instr_data_ofs = voice[i].instr_loop_start + (voice[i].instr_data_ofs - voice[i].instr_data_end); voice[i].instr_data_end = voice[i].instr_loop_start + voice[i].instr_loop_count; } if (voice[i].instr_data_skip >= voice[i].instr_data_count) { voice[i].instr_data_count = voice[i].instr_loop_count; voice[i].instr_data_ofs = voice[i].instr_loop_start; } else { voice[i].instr_data_count -= voice[i].instr_data_skip; } } } while (--dma_cnt); } void fill_dma_08_2(void) { u16 dma_cnt; int i; dma_cnt = chunk_size; do { dma_buf08[dma_head] = 0x80 + (amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))] + amp_table[voice[2].amplitude / 16][*(voice[2].data + (voice[2].instr_data_ofs / 256))]) / 128; dma_head = (dma_head + 1) & DMA_WRAP; dma_buf08[dma_head] = 0x80 + (amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))] + amp_table[voice[3].amplitude / 16][*(voice[3].data + (voice[3].instr_data_ofs / 256))]) / 128; dma_head = (dma_head + 1) & DMA_WRAP; for (i = 0; i < D_MAX_INST; i++) { voice[i].instr_data_ofs += voice[i].instr_data_skip; if (voice[i].instr_data_ofs >= voice[i].instr_data_end) { voice[i].instr_data_ofs = voice[i].instr_loop_start + (voice[i].instr_data_ofs - voice[i].instr_data_end); voice[i].instr_data_end = voice[i].instr_loop_start + voice[i].instr_loop_count; } if (voice[i].instr_data_skip >= voice[i].instr_data_count) { voice[i].instr_data_count = voice[i].instr_loop_count; voice[i].instr_data_ofs = voice[i].instr_loop_start; } else { voice[i].instr_data_count -= voice[i].instr_data_skip; } } } while (--dma_cnt); } void fill_dma_08_2_w(void) { u16 dma_cnt; int i; dma_cnt = chunk_size; do { dma_buf08[dma_head] = 0x80 + (amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))] + amp_table[voice[2].amplitude / 16][*(voice[2].data + (voice[2].instr_data_ofs / 256))] - amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))]) / 128; dma_head = (dma_head + 1) & DMA_WRAP; dma_buf08[dma_head] = 0x80 + (amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))] + amp_table[voice[3].amplitude / 16][*(voice[3].data + (voice[3].instr_data_ofs / 256))] - amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))]) / 128; dma_head = (dma_head + 1) & DMA_WRAP; for (i = 0; i < D_MAX_INST; i++) { voice[i].instr_data_ofs += voice[i].instr_data_skip; if (voice[i].instr_data_ofs >= voice[i].instr_data_end) { voice[i].instr_data_ofs = voice[i].instr_loop_start + (voice[i].instr_data_ofs - voice[i].instr_data_end); voice[i].instr_data_end = voice[i].instr_loop_start + voice[i].instr_loop_count; } if (voice[i].instr_data_skip >= voice[i].instr_data_count) { voice[i].instr_data_count = voice[i].instr_loop_count; voice[i].instr_data_ofs = voice[i].instr_loop_start; } else { voice[i].instr_data_count -= voice[i].instr_data_skip; } } } while (--dma_cnt); } void fill_dma_16_1(void) { u16 dma_cnt; int i; dma_cnt = chunk_size; do { dma_buf16[dma_head] = amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))] + amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))] + amp_table[voice[2].amplitude / 16][*(voice[2].data + (voice[2].instr_data_ofs / 256))] + amp_table[voice[3].amplitude / 16][*(voice[3].data + (voice[3].instr_data_ofs / 256))]; dma_head = (dma_head + 1) & DMA_WRAP; for (i = 0; i < D_MAX_INST; i++) { voice[i].instr_data_ofs += voice[i].instr_data_skip; if (voice[i].instr_data_ofs >= voice[i].instr_data_end) { voice[i].instr_data_ofs = voice[i].instr_loop_start + (voice[i].instr_data_ofs - voice[i].instr_data_end); voice[i].instr_data_end = voice[i].instr_loop_start + voice[i].instr_loop_count; } if (voice[i].instr_data_skip >= voice[i].instr_data_count) { voice[i].instr_data_count = voice[i].instr_loop_count; voice[i].instr_data_ofs = voice[i].instr_loop_start; } else { voice[i].instr_data_count -= voice[i].instr_data_skip; } } } while (--dma_cnt); } void fill_dma_16_2(void) { u16 dma_cnt; int i; dma_cnt = chunk_size; do { dma_buf16[dma_head] = amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))] + amp_table[voice[2].amplitude / 16][*(voice[2].data + (voice[2].instr_data_ofs / 256))]; dma_head = (dma_head + 1) & DMA_WRAP; dma_buf16[dma_head] = amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))] + amp_table[voice[3].amplitude / 16][*(voice[3].data + (voice[3].instr_data_ofs / 256))]; dma_head = (dma_head + 1) & DMA_WRAP; for (i = 0; i < D_MAX_INST; i++) { voice[i].instr_data_ofs += voice[i].instr_data_skip; if (voice[i].instr_data_ofs >= voice[i].instr_data_end) { voice[i].instr_data_ofs = voice[i].instr_loop_start + (voice[i].instr_data_ofs - voice[i].instr_data_end); voice[i].instr_data_end = voice[i].instr_loop_start + voice[i].instr_loop_count; } if (voice[i].instr_data_skip >= voice[i].instr_data_count) { voice[i].instr_data_count = voice[i].instr_loop_count; voice[i].instr_data_ofs = voice[i].instr_loop_start; } else { voice[i].instr_data_count -= voice[i].instr_data_skip; } } } while (--dma_cnt); } void fill_dma_16_2_w(void) { u16 dma_cnt; int i; dma_cnt = chunk_size; do { dma_buf16[dma_head] = amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))] + amp_table[voice[2].amplitude / 16][*(voice[2].data + (voice[2].instr_data_ofs / 256))] - amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))]; dma_head = (dma_head + 1) & DMA_WRAP; dma_buf16[dma_head] = amp_table[voice[1].amplitude / 16][*(voice[1].data + (voice[1].instr_data_ofs / 256))] + amp_table[voice[3].amplitude / 16][*(voice[3].data + (voice[3].instr_data_ofs / 256))] - amp_table[voice[0].amplitude / 16][*(voice[0].data + (voice[0].instr_data_ofs / 256))]; dma_head = (dma_head + 1) & DMA_WRAP; for (i = 0; i < D_MAX_INST; i++) { voice[i].instr_data_ofs += voice[i].instr_data_skip; if (voice[i].instr_data_ofs >= voice[i].instr_data_end) { voice[i].instr_data_ofs = voice[i].instr_loop_start + (voice[i].instr_data_ofs - voice[i].instr_data_end); voice[i].instr_data_end = voice[i].instr_loop_start + voice[i].instr_loop_count; } if (voice[i].instr_data_skip >= voice[i].instr_data_count) { voice[i].instr_data_count = voice[i].instr_loop_count; voice[i].instr_data_ofs = voice[i].instr_loop_start; } else { voice[i].instr_data_count -= voice[i].instr_data_skip; } } } while (--dma_cnt); } void init_module(void) { int i, j; for (i = 0; i <= D_MAX_AMP / 16; i++) { for (j = -128; j < 128; j++) { amp_table[i][(u8) j] = i * j * 7 / 8; } } for (i = 0; i < 4; i++) { voice[i].ctrl_function = ctrl_nothing; voice[i].data = dummy; voice[i].instr_data_count = 0; voice[i].amplitude = 0; } freq_master = 21222 * 65536 / sample_rate; chunk_size = sample_rate * 60 / module_speed / 30; i = tot_cnt * chunk_size / sample_rate; j = i % 60; i = i / 60; if (verbose) { printf("mod speed : %d bpm\n", module_speed); printf("divisor : %d (a'= %d)\n", freq_master, freq_master / 440); printf("chunk size : %d samples\n", chunk_size); printf("play time : %d:%02d\n", i, j); } rep_max = D_REPEAT_CNT; rep_add = 0; rep_cnt = 0; tot_cnt = 0; track_new = 0; trk_idx = 0; track_skip = skp_fix; trk_ofs = 0; trk_ply = track_index[0]; mod_don = 0; dma_head = dma_tail = dma_count = 0; } void done_module(void) { int i; for (i = 0; i < 31; i++) { if (instr[i]) free(instr[i]); instr[i] = (u8 *) 0; } } void play_module(void) { if (dma_count + (chunk_size * sample_channels) <= DMA_SIZE) { if (track_new || track_skip) { track_new = 0; trk_ofs = 0; trk_idx++; trk_ply = track_index[trk_idx]; if (trk_idx >= track_count) mod_don = 1; if (track_skip) track_skip--; } ++tot_cnt; if ((play_intro) && (tot_cnt > 128 * D_REPEAT_CNT)) mod_don = 1; if ((--rep_cnt < 0) || (track_skip)) { if (rep_add) { rep_cnt = rep_add; rep_add = 0; play_chg_note(); } else { rep_cnt = rep_max; play_new_note(); } } else { play_chg_note(); } if (!track_skip) { if (sample_bits == 16) { if (sample_channels == 1) { fill_dma_16_1(); } else { if (stereo_wide) fill_dma_16_2_w(); else fill_dma_16_2(); } } else { if (sample_channels == 1) { fill_dma_08_1(); } else { if (stereo_wide) fill_dma_08_2_w(); else fill_dma_08_2(); } } dma_count += chunk_size * sample_channels; } /* ! track_skip */ } if (dma_count >= DMA_HALF) { if (sample_bits == 16) { write(dsp_device, &dma_buf16[dma_tail], DMA_HALF * 2); } else { write(dsp_device, &dma_buf08[dma_tail], DMA_HALF); } dma_tail = (dma_tail + DMA_HALF) & DMA_WRAP; dma_count -= DMA_HALF; } } void init_dsp(void) { int ioresult; dsp_device = open(dsp_device_name, O_RDWR); ioctl(dsp_device, SOUND_PCM_WRITE_CHANNELS, &sample_channels); ioctl(dsp_device, SNDCTL_DSP_SAMPLESIZE, &sample_bits); ioctl(dsp_device, SNDCTL_DSP_SPEED, &sample_rate); ioctl(dsp_device, SOUND_PCM_READ_CHANNELS, &sample_channels); ioctl(dsp_device, SOUND_PCM_READ_BITS, &sample_bits); ioctl(dsp_device, SOUND_PCM_READ_RATE, &sample_rate); if (verbose) { printf("device : %s\n", dsp_device_name); printf("channels : %d %s\n", sample_channels, stereo_wide ? "wide" : "narrow"); printf("sample rate: %d samples/sec\n", sample_rate); printf("sample size: %d bit\n", sample_bits); } } void exit_dsp(void) { if (dsp_device) close(dsp_device); } void init_mixer(void) { int mixer_device; int vol = 0xffff; mixer_device = open("/dev/mixer", O_RDWR); ioctl(mixer_device, SOUND_MIXER_WRITE_VOLUME, &vol); close(mixer_device); } int main(int ac, char **av) { int a; init_mixer(); memset(instr, 0, sizeof(instr)); if (ac > 1) { a = 1; while (a < ac) { if (strcmp(av[a], "-b") == 0) { a++; sample_bits = atoi(av[a]); } else if (strcmp(av[a], "-c") == 0) { a++; sample_channels = atoi(av[a]); } else if (strcmp(av[a], "-d") == 0) { a++; strncpy(dsp_device_name, av[a], 127); } else if (strcmp(av[a], "-i") == 0) { play_intro = 1; } else if (strcmp(av[a], "-m") == 0) { a++; bpm_fix = atoi(av[a]); } else if (strcmp(av[a], "-o") == 0) { a++; skp_fix = atoi(av[a]); } else if (strcmp(av[a], "-s") == 0) { a++; sample_rate = atoi(av[a]); } else if (strcmp(av[a], "-t") == 0) { print_tracks = 1; } else if (strcmp(av[a], "-v") == 0) { verbose = 1; } else if (strcmp(av[a], "-w") == 0) { a++; stereo_wide = atoi(av[a]); } else { if (read_module(av[a])) { init_dsp(); init_module(); do { play_module(); } while (!mod_don); done_module(); exit_dsp(); } } a++; } } else { printf("usage : %s [-b bits] [-c channels] [-d device] [-o tracks]\n", av[0]); printf(" [-s samplerate] [-v] [-w wide] module\n"); printf(" - b bit 8 or 16\n"); printf(" - c channels 1 or 2\n"); printf(" - d device /dev/dsp, /dev/dsp16 or any filename\n"); printf(" - i play intro of selected mod files\n"); printf(" - m modspeed override mod speed (beats per min)\n"); printf(" - o tracks tracks to skip (offset)\n"); printf(" - s samplerate 5000 to 22050 [eventually 44100] Hz\n"); printf(" - t display track data while playing\n"); printf(" - v verbose (mod file & setup info)\n"); printf(" - w wide 0 or 1 (only if channels == 2)\n"); printf("e.g.: %s -d /dev/dsp -s 44100 -c 1 -b 8 klisje.mod\n", av[0]); } exit(0); }