forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			224 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			224 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#import <AudioToolbox/AudioToolbox.h>
							 | 
						||
| 
								 | 
							
								#import <Foundation/Foundation.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <kinc/audio2/audio.h>
							 | 
						||
| 
								 | 
							
								#include <kinc/backend/video.h>
							 | 
						||
| 
								 | 
							
								#include <kinc/math/core.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include <stdio.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define kOutputBus 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static kinc_internal_video_sound_stream_t *video = NULL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void iosPlayVideoSoundStream(kinc_internal_video_sound_stream_t *v) {
							 | 
						||
| 
								 | 
							
									video = v;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void iosStopVideoSoundStream(void) {
							 | 
						||
| 
								 | 
							
									video = NULL;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void affirm(OSStatus err) {
							 | 
						||
| 
								 | 
							
									if (err) {
							 | 
						||
| 
								 | 
							
										fprintf(stderr, "Error: %i\n", (int)err);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool initialized;
							 | 
						||
| 
								 | 
							
								static bool soundPlaying;
							 | 
						||
| 
								 | 
							
								static AudioStreamBasicDescription deviceFormat;
							 | 
						||
| 
								 | 
							
								static AudioComponentInstance audioUnit;
							 | 
						||
| 
								 | 
							
								static bool isFloat = false;
							 | 
						||
| 
								 | 
							
								static bool isInterleaved = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static kinc_a2_buffer_t a2_buffer;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void copySample(void *buffer, void *secondary_buffer) {
							 | 
						||
| 
								 | 
							
									float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
							 | 
						||
| 
								 | 
							
									float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
							 | 
						||
| 
								 | 
							
									a2_buffer.read_location += 1;
							 | 
						||
| 
								 | 
							
									if (a2_buffer.read_location >= a2_buffer.data_size) {
							 | 
						||
| 
								 | 
							
										a2_buffer.read_location = 0;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (video != NULL) {
							 | 
						||
| 
								 | 
							
										float *frame = kinc_internal_video_sound_stream_next_frame(video);
							 | 
						||
| 
								 | 
							
										left_value += frame[0];
							 | 
						||
| 
								 | 
							
										left_value = kinc_max(kinc_min(left_value, 1.0f), -1.0f);
							 | 
						||
| 
								 | 
							
										right_value += frame[1];
							 | 
						||
| 
								 | 
							
										right_value = kinc_max(kinc_min(right_value, 1.0f), -1.0f);
							 | 
						||
| 
								 | 
							
										if (kinc_internal_video_sound_stream_ended(video)) {
							 | 
						||
| 
								 | 
							
											video = NULL;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (secondary_buffer == NULL) {
							 | 
						||
| 
								 | 
							
										if (isFloat) {
							 | 
						||
| 
								 | 
							
											((float *)buffer)[0] = left_value;
							 | 
						||
| 
								 | 
							
											((float *)buffer)[1] = right_value;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											((int16_t *)buffer)[0] = (int16_t)(left_value * 32767);
							 | 
						||
| 
								 | 
							
											((int16_t *)buffer)[1] = (int16_t)(right_value * 32767);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else {
							 | 
						||
| 
								 | 
							
										if (isFloat) {
							 | 
						||
| 
								 | 
							
											*(float *)buffer = left_value;
							 | 
						||
| 
								 | 
							
											*(float *)secondary_buffer = right_value;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											*(int16_t *)buffer = (int16_t)(left_value * 32767);
							 | 
						||
| 
								 | 
							
											*(int16_t *)secondary_buffer = (int16_t)(right_value * 32767);
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static OSStatus renderInput(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
							 | 
						||
| 
								 | 
							
								                            UInt32 inNumberFrames, AudioBufferList *outOutputData) {
							 | 
						||
| 
								 | 
							
									kinc_a2_internal_callback(&a2_buffer, inNumberFrames);
							 | 
						||
| 
								 | 
							
									if (isInterleaved) {
							 | 
						||
| 
								 | 
							
										if (isFloat) {
							 | 
						||
| 
								 | 
							
											float *output = (float *)outOutputData->mBuffers[0].mData;
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < inNumberFrames; ++i) {
							 | 
						||
| 
								 | 
							
												copySample(output, NULL);
							 | 
						||
| 
								 | 
							
												output += 2;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											int16_t *output = (int16_t *)outOutputData->mBuffers[0].mData;
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < inNumberFrames; ++i) {
							 | 
						||
| 
								 | 
							
												copySample(output, NULL);
							 | 
						||
| 
								 | 
							
												output += 2;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									else {
							 | 
						||
| 
								 | 
							
										if (isFloat) {
							 | 
						||
| 
								 | 
							
											float *out1 = (float *)outOutputData->mBuffers[0].mData;
							 | 
						||
| 
								 | 
							
											float *out2 = (float *)outOutputData->mBuffers[1].mData;
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < inNumberFrames; ++i) {
							 | 
						||
| 
								 | 
							
												copySample(out1++, out2++);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										else {
							 | 
						||
| 
								 | 
							
											int16_t *out1 = (int16_t *)outOutputData->mBuffers[0].mData;
							 | 
						||
| 
								 | 
							
											int16_t *out2 = (int16_t *)outOutputData->mBuffers[1].mData;
							 | 
						||
| 
								 | 
							
											for (int i = 0; i < inNumberFrames; ++i) {
							 | 
						||
| 
								 | 
							
												copySample(out1++, out2++);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return noErr;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static uint32_t samples_per_second = 44100;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void sampleRateListener(void *inRefCon, AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement) {
							 | 
						||
| 
								 | 
							
									Float64 sampleRate;
							 | 
						||
| 
								 | 
							
									UInt32 size = sizeof(sampleRate);
							 | 
						||
| 
								 | 
							
									affirm(AudioUnitGetProperty(inUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate, &size));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (samples_per_second != (uint32_t)sampleRate) {
							 | 
						||
| 
								 | 
							
										samples_per_second = (uint32_t)sampleRate;
							 | 
						||
| 
								 | 
							
										kinc_a2_internal_sample_rate_callback();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static bool initialized = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_a2_init(void) {
							 | 
						||
| 
								 | 
							
									if (initialized) {
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									kinc_a2_internal_init();
							 | 
						||
| 
								 | 
							
									initialized = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									a2_buffer.read_location = 0;
							 | 
						||
| 
								 | 
							
									a2_buffer.write_location = 0;
							 | 
						||
| 
								 | 
							
									a2_buffer.data_size = 128 * 1024;
							 | 
						||
| 
								 | 
							
									a2_buffer.channel_count = 2;
							 | 
						||
| 
								 | 
							
									a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float));
							 | 
						||
| 
								 | 
							
									a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									initialized = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									AudioComponentDescription desc;
							 | 
						||
| 
								 | 
							
									desc.componentType = kAudioUnitType_Output;
							 | 
						||
| 
								 | 
							
									desc.componentSubType = kAudioUnitSubType_RemoteIO;
							 | 
						||
| 
								 | 
							
									desc.componentFlags = 0;
							 | 
						||
| 
								 | 
							
									desc.componentFlagsMask = 0;
							 | 
						||
| 
								 | 
							
									desc.componentManufacturer = kAudioUnitManufacturer_Apple;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									AudioComponent comp = AudioComponentFindNext(NULL, &desc);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Get audio units
							 | 
						||
| 
								 | 
							
									affirm(AudioComponentInstanceNew(comp, &audioUnit));
							 | 
						||
| 
								 | 
							
									UInt32 flag = 1;
							 | 
						||
| 
								 | 
							
									affirm(AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(UInt32)));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (soundPlaying)
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									affirm(AudioOutputUnitStart(audioUnit));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									UInt32 size = sizeof(AudioStreamBasicDescription);
							 | 
						||
| 
								 | 
							
									affirm(AudioUnitGetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &deviceFormat, &size));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (deviceFormat.mFormatID != kAudioFormatLinearPCM) {
							 | 
						||
| 
								 | 
							
										fprintf(stderr, "mFormatID !=  kAudioFormatLinearPCM\n");
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (deviceFormat.mFormatFlags & kLinearPCMFormatFlagIsFloat) {
							 | 
						||
| 
								 | 
							
										isFloat = true;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (deviceFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) {
							 | 
						||
| 
								 | 
							
										isInterleaved = false;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									AudioUnitAddPropertyListener(audioUnit, kAudioUnitProperty_StreamFormat, sampleRateListener, nil);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									initialized = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									printf("mSampleRate = %g\n", deviceFormat.mSampleRate);
							 | 
						||
| 
								 | 
							
									printf("mFormatFlags = %08X\n", (unsigned int)deviceFormat.mFormatFlags);
							 | 
						||
| 
								 | 
							
									printf("mBytesPerPacket = %d\n", (unsigned int)deviceFormat.mBytesPerPacket);
							 | 
						||
| 
								 | 
							
									printf("mFramesPerPacket = %d\n", (unsigned int)deviceFormat.mFramesPerPacket);
							 | 
						||
| 
								 | 
							
									printf("mChannelsPerFrame = %d\n", (unsigned int)deviceFormat.mChannelsPerFrame);
							 | 
						||
| 
								 | 
							
									printf("mBytesPerFrame = %d\n", (unsigned int)deviceFormat.mBytesPerFrame);
							 | 
						||
| 
								 | 
							
									printf("mBitsPerChannel = %d\n", (unsigned int)deviceFormat.mBitsPerChannel);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (samples_per_second != (uint32_t)deviceFormat.mSampleRate) {
							 | 
						||
| 
								 | 
							
										samples_per_second = (uint32_t)deviceFormat.mSampleRate;
							 | 
						||
| 
								 | 
							
										kinc_a2_internal_sample_rate_callback();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									AURenderCallbackStruct callbackStruct;
							 | 
						||
| 
								 | 
							
									callbackStruct.inputProc = renderInput;
							 | 
						||
| 
								 | 
							
									callbackStruct.inputProcRefCon = NULL;
							 | 
						||
| 
								 | 
							
									affirm(AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct)));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									soundPlaying = true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_a2_update(void) {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void kinc_a2_shutdown(void) {
							 | 
						||
| 
								 | 
							
									if (!initialized)
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
									if (!soundPlaying)
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									affirm(AudioOutputUnitStop(audioUnit));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									soundPlaying = false;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								uint32_t kinc_a2_samples_per_second(void) {
							 | 
						||
| 
								 | 
							
									return samples_per_second;
							 | 
						||
| 
								 | 
							
								}
							 |