#include #include #include #include #include #include #include "heap.h" #include "noise.h" #undef OSCILLOSCOPE #undef STDOUT static int next_sample(struct player_t* player, sample_t** sample); static int next_note(struct player_t* player, struct channel_t* channel); /* Return a new music player */ struct player_t* create_player(int length, int bits, int stereo, int rate, int vol, int pan, int max_voices) { AUDIOSTREAM* stream = play_audio_stream(length, bits, stereo, rate, vol, pan); struct player_t* player = malloc(sizeof(struct player_t)); if (stream == NULL || player == NULL) return NULL; player->channels = create_basic_heap(struct channel_t); if (player->channels == NULL) { stop_audio_stream(stream); free(player); return NULL; } player->voices = create_fixed_heap(struct voice_t, max_voices); if (player->voices == NULL) { stop_audio_stream(stream); destroy_heap(player->channels); free(player); return NULL; } player->stream = stream; player->max_voices = max_voices; player->buf_length = length; player->bits = bits; player->stereo = stereo; player->rate = rate; player->vol = vol; player->pan = pan; player->max_sample = (1 << bits) - 1; player->exp_buf = malloc(sizeof(void*) * max_voices); return player; } /* Destroy a music player */ void destroy_player(struct player_t* player) { destroy_heap(player->channels); destroy_heap(player->voices); stop_audio_stream(player->stream); free(player); } /* Add a channel to a player */ void add_channel(struct player_t* player, struct note_t* notes, int num_notes, int repeat, double vol, double pan, struct instrument_t* instrument, int tempo, double beat_note, int transpose) { struct channel_t* channel = new_from_heap(player->channels); channel->notes = notes; channel->num_notes = num_notes; channel->note_idx = -1; channel->remain = 0; channel->repeat = repeat; channel->vol = vol; channel->pan = pan; channel->instrument = instrument; channel->tempo = tempo; channel->beat_note = beat_note; channel->transpose = transpose; next_note(player, channel); if (player->channels->count > player->max_voices) player->exp_buf = realloc(player->exp_buf, sizeof(void*) * player->channels->count); } /* Add a channel to a player */ void add_channel_struct(struct player_t* player, struct channel_t* channel) { add_channel(player, channel->notes, channel->num_notes, channel->repeat, channel->vol, channel->pan, channel->instrument, channel->tempo, channel->beat_note, channel->transpose); } /* Fill a player's audio buffer */ int fill_buffer(struct player_t* player) { sample_t* p = get_audio_stream_buffer(player->stream); sample_t* p_end; int retval = 1; if (p == NULL) return 1; /* Fill the buffer, sample by sample */ p_end = p + player->buf_length * (1 + player->stereo); while (p < p_end) { if (!next_sample(player, &p)) { /* No more music! */ bzero(p, p_end - p); retval = 0; break; } } free_audio_stream_buffer(player->stream); return retval; } /* Grab the next sample from the channels */ static int next_sample(struct player_t* player, sample_t** sample) { static int j, old; int i; double amp; double sample_l = 0; double sample_r = 0; #ifdef STDOUT int val; #endif if (player->voices->count > 0) { /* Collect samples from voices */ struct voice_t** voice_p = (struct voice_t**)player->voices->data; struct voice_t** voice_end = voice_p + player->voices->count; for (; voice_p < voice_end; voice_p++) { struct voice_t* voice = *voice_p; amp = voice->instrument->func(voice) * voice->vol; sample_l += amp * voice->pan; sample_r += amp * (1 - voice->pan); voice->step++; if (voice->step == voice->dur) player->exp_buf[player->num_exp++] = voice; } /* Store samples in buffer */ *((*sample)++) = player->max_sample * sample_l / player->max_voices; *((*sample)++) = player->max_sample * sample_r / player->max_voices; #ifdef STDOUT val = (sample_l + sample_r) * 65535 / 2; printf("%c%c", val & 255, val >> 8); #endif #ifdef OSCILLOSCOPE if (j == 0) {printf("\033[2J\033[H"); fflush(stdout);} if (j < 24 * 12 && j % 12 == 0) { int x = (sample_l + sample_r) * 75 / player->max_voices; printf("%.3f ", (sample_l + sample_r) / (2 * player->max_voices)); if (old < x) { for (i = 0; i < old; i++) printf(":"); printf("`"); for (i = old + 1; i < x; i++) printf("-"); printf("."); } else if (old == x) { for (i = 0; i < x; i++) printf(":"); printf("|"); } else { for (i = 0; i < x; i++) printf(":"); printf("."); for (i = x + 1; i < old; i++) printf("-"); printf("'"); } printf("\n"); fflush(stdout); old = x; } #endif j++; /* Kill expired voices */ for (i = 0; i < player->num_exp; i++) delete_from_heap(player->voices, player->exp_buf[i]); player->num_exp = 0; } else { *((*sample)++) = 0; *((*sample)++) = 0; } if (player->channels->count > 0) { /* Progress through notes */ struct channel_t** channel_p = (struct channel_t**)player->channels->data; struct channel_t** channel_end = channel_p + player->channels->count; for (; channel_p < channel_end; channel_p++) { struct channel_t* channel = *channel_p; channel->remain--; if (channel->remain != 0) continue; if (!next_note(player, channel)) player->exp_buf[player->num_exp++] = channel; j = 0; } /* Kill expired channels */ for (i = 0; i < player->num_exp; i++) { delete_from_heap(player->channels, player->exp_buf[i]); } player->num_exp = 0; } /* Is there anything left to play? */ return player->voices->count > 0 || player->channels->count > 0; } /* Go to the next note in a song */ static int next_note(struct player_t* player, struct channel_t* channel) { struct voice_t* voice; int val; int dur; channel->note_idx++; if (channel->note_idx == channel->num_notes) { /* All done with this channel? */ if (channel->repeat == 0) return 0; /* Repeat channel */ channel->note_idx = 0; if (channel->repeat > 0) channel->repeat--; } /* Start next note or rest */ val = channel->notes[channel->note_idx].val; dur = player->rate * 60 / channel->tempo / channel->beat_note * channel->notes[channel->note_idx].dur; if (val > INT_MIN) { /* Start next note */ voice = new_from_heap(player->voices); if (voice != NULL) { val += channel->transpose; voice->freq = 220 * pow(2, val / 12.0); voice->period = player->rate / voice->freq; voice->step = 0; voice->dur = dur * channel->instrument->sustain; voice->vol = channel->vol; voice->pan = channel->pan; voice->instrument = channel->instrument; } } channel->remain = dur; return 1; }