|
|
@@ -0,0 +1,173 @@
|
|
|
+#include "../headers/mandarin.h"
|
|
|
+#include "../headers/yuzuparse.h"
|
|
|
+
|
|
|
+int portaudio_callback(const void* input, void* output, unsigned long frame_count, const PaStreamCallbackTimeInfo* pa_time_info, PaStreamCallbackFlags pa_status_flags, void* audio_data){
|
|
|
+ pa_callback_data* data = (pa_callback_data*)audio_data;
|
|
|
+
|
|
|
+ float* out = (float*)output;
|
|
|
+
|
|
|
+ for(uint32_t i = 0; i < frame_count; i++){
|
|
|
+ for (uint32_t j = 0; j < data->channel_count; j++) {
|
|
|
+ *out++ = data->pcm_data[(int)(data->current_sample) * data->channel_count + j];
|
|
|
+ }
|
|
|
+
|
|
|
+ data->current_sample += *data->speed_multiplier;
|
|
|
+
|
|
|
+ if (*data->should_loop_audio){
|
|
|
+ if (data->current_sample >= data->sample_count){
|
|
|
+ data->current_sample = data->sample_count - data->current_sample;
|
|
|
+ } else if (data->current_sample < 0){
|
|
|
+ data->current_sample = data->sample_count + data->current_sample;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data->current_sample >= data->sample_count || data->current_sample < 0){
|
|
|
+ goto END;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data->current_sample >= data->sample_count){
|
|
|
+ goto END;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ END:
|
|
|
+ if (data->end_function) data->end_function(data->custom_pointer_for_end_function);
|
|
|
+
|
|
|
+ free(audio_data);
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+int tone_callback(const void* input, void* output, unsigned long frames_per_buffer, const PaStreamCallbackTimeInfo* time_info, PaStreamFlags status_flags, void* user_data){
|
|
|
+ pa_tone_callback_data* data = (pa_tone_callback_data*)user_data;
|
|
|
+ float *out = (float*)output;
|
|
|
+ unsigned long i;
|
|
|
+
|
|
|
+ for(i = 0; i < frames_per_buffer; i++){
|
|
|
+ *out++ = data->left_tone;
|
|
|
+ *out++ = data->right_tone;
|
|
|
+
|
|
|
+ float delta = data->tone_generator((double)data->current_sample / (double)data->sample_rate, data->value);
|
|
|
+
|
|
|
+ data->left_tone = delta;
|
|
|
+ if(data->left_tone >= 1.0) data->left_tone = 1.0;
|
|
|
+ if(data->left_tone <= -1.0) data->left_tone = -1.0;
|
|
|
+
|
|
|
+ data->right_tone = delta;
|
|
|
+ if(data->right_tone >= 1.0) data->right_tone = 1.0;
|
|
|
+ if(data->right_tone <= -1.0) data->right_tone = -1.0;
|
|
|
+
|
|
|
+ data->current_sample++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data->current_sample >= data->sample_count){
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+unsigned char player_is_initialized = 0;
|
|
|
+
|
|
|
+void MD_init_player(){
|
|
|
+ if (player_is_initialized){
|
|
|
+ printf("cannot init, already started");
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ PaError error;
|
|
|
+
|
|
|
+ // block PortAudio's standard output and standard error stream
|
|
|
+ freopen("/dev/null", "w", stdout);
|
|
|
+ freopen("/dev/null", "w", stderr);
|
|
|
+
|
|
|
+ error = Pa_Initialize();
|
|
|
+
|
|
|
+ // unblock PortAudio's standard output and standard error stream
|
|
|
+ freopen("/dev/tty", "w", stdout);
|
|
|
+ freopen("/dev/tty", "w", stderr);
|
|
|
+
|
|
|
+ if(error != paNoError) { printf("PortAudio error: %d\n", error); exit(1);}
|
|
|
+
|
|
|
+ player_is_initialized = 1;
|
|
|
+}
|
|
|
+
|
|
|
+void MD_kill_player(){
|
|
|
+ if (!player_is_initialized){
|
|
|
+ printf("cannot kill, player isn't initilized");
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ PaError error;
|
|
|
+
|
|
|
+ error = Pa_Terminate();
|
|
|
+ if(error != paNoError) { printf("PortAudio error: %d\n", error); exit(1);}
|
|
|
+}
|
|
|
+
|
|
|
+pa_callback_data* play_stream(YZ_audio_stream* audio_stream, double* pitch_multiplier, double* speed_multiplier, unsigned char* should_loop_audio, double starting_second, void (*end_func)(void* custom_pointer_for_end_function), void* custom_pointer_for_end_function){
|
|
|
+ PaError error;
|
|
|
+
|
|
|
+ PaStream *stream;
|
|
|
+
|
|
|
+ pa_callback_data* callback_data = malloc(sizeof(pa_callback_data));
|
|
|
+
|
|
|
+ callback_data->channel_count = audio_stream->channel_count;
|
|
|
+ callback_data->current_sample = starting_second >= 0 ? audio_stream->sample_rate * starting_second : audio_stream->sample_count;
|
|
|
+ callback_data->pcm_data = audio_stream->pcm_data;
|
|
|
+ callback_data->sample_count = audio_stream->sample_count;
|
|
|
+ callback_data->pitch_multiplier = pitch_multiplier;
|
|
|
+ callback_data->speed_multiplier = speed_multiplier;
|
|
|
+ callback_data->should_loop_audio = should_loop_audio;
|
|
|
+ callback_data->end_function = end_func;
|
|
|
+ callback_data->custom_pointer_for_end_function = custom_pointer_for_end_function;
|
|
|
+
|
|
|
+ if (!player_is_initialized) MD_init_player();
|
|
|
+
|
|
|
+ error = Pa_OpenDefaultStream(&stream, 0, audio_stream->channel_count, paFloat32, audio_stream->sample_rate, paFramesPerBufferUnspecified, portaudio_callback, callback_data);
|
|
|
+ if(error != paNoError) { printf("PortAudio error: %d\n", error); exit(1);}
|
|
|
+
|
|
|
+ Pa_StartStream(stream);
|
|
|
+
|
|
|
+ return callback_data;
|
|
|
+}
|
|
|
+
|
|
|
+// play an audio stream
|
|
|
+pa_callback_data* MD_play_stream(YZ_audio_stream* audio_stream){
|
|
|
+ double value = 1;
|
|
|
+ unsigned char antivalue = 0;
|
|
|
+
|
|
|
+ return play_stream(audio_stream, &value, &value, &antivalue, (double)antivalue, NULL, NULL);
|
|
|
+}
|
|
|
+
|
|
|
+// play an audio stream and modify pitch and speed dynamically. Pitch and speed changes work in real time due to it being pointers to the values
|
|
|
+pa_callback_data* MD_play_stream_dynamic(YZ_audio_stream* audio_stream, double* speed_multiplier, unsigned char* should_loop_audio, double starting_second, void (*end_func)(void*), void* custom_pointer_for_end_function){
|
|
|
+ return play_stream(audio_stream, NULL, speed_multiplier, should_loop_audio, starting_second, end_func, custom_pointer_for_end_function);
|
|
|
+}
|
|
|
+
|
|
|
+pa_tone_callback_data* MD_play_tone(float (*tone_generator)(float, float), float sample_rate, float duration, float value){
|
|
|
+ if (!player_is_initialized) MD_init_player();
|
|
|
+
|
|
|
+ pa_tone_callback_data* callback_data = malloc(sizeof(pa_tone_callback_data));
|
|
|
+
|
|
|
+ callback_data->tone_generator = tone_generator;
|
|
|
+ callback_data->current_sample = 0;
|
|
|
+ callback_data->duration = duration;
|
|
|
+ callback_data->left_amplitude = 1;
|
|
|
+ callback_data->right_amplitude = 1;
|
|
|
+ callback_data->sample_rate = sample_rate;
|
|
|
+ callback_data->sample_count = duration * sample_rate;
|
|
|
+ callback_data->value = value;
|
|
|
+
|
|
|
+ PaStream* stream;
|
|
|
+
|
|
|
+ PaError error = Pa_OpenDefaultStream(&stream, 0, 2, paFloat32, sample_rate, paFramesPerBufferUnspecified, tone_callback, callback_data);
|
|
|
+ if(error != paNoError) { printf("PortAudio error: %d\n", error); exit(1);}
|
|
|
+
|
|
|
+ Pa_StartStream(stream);
|
|
|
+
|
|
|
+ return callback_data;
|
|
|
+}
|