forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			159 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			159 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | #include <CoreAudio/AudioHardware.h>
 | ||
|  | #include <CoreServices/CoreServices.h>
 | ||
|  | 
 | ||
|  | #include <kinc/audio2/audio.h>
 | ||
|  | #include <kinc/backend/video.h>
 | ||
|  | #include <kinc/log.h>
 | ||
|  | 
 | ||
|  | #include <stdio.h>
 | ||
|  | 
 | ||
|  | static kinc_internal_video_sound_stream_t *video = NULL; | ||
|  | 
 | ||
|  | void macPlayVideoSoundStream(kinc_internal_video_sound_stream_t *v) { | ||
|  | 	video = v; | ||
|  | } | ||
|  | 
 | ||
|  | void macStopVideoSoundStream(void) { | ||
|  | 	video = NULL; | ||
|  | } | ||
|  | 
 | ||
|  | static void affirm(OSStatus err) { | ||
|  | 	if (err != kAudioHardwareNoError) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Error: %i\n", err); | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | static bool initialized; | ||
|  | static bool soundPlaying; | ||
|  | static AudioDeviceID device; | ||
|  | static UInt32 deviceBufferSize; | ||
|  | static UInt32 size; | ||
|  | static AudioStreamBasicDescription deviceFormat; | ||
|  | static AudioObjectPropertyAddress address; | ||
|  | 
 | ||
|  | static AudioDeviceIOProcID theIOProcID = NULL; | ||
|  | 
 | ||
|  | static kinc_a2_buffer_t a2_buffer; | ||
|  | 
 | ||
|  | static uint32_t samples_per_second = 44100; | ||
|  | 
 | ||
|  | uint32_t kinc_a2_samples_per_second(void) { | ||
|  | 	return samples_per_second; | ||
|  | } | ||
|  | 
 | ||
|  | static void copySample(void *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; | ||
|  | 	} | ||
|  | 	((float *)buffer)[0] = left_value; | ||
|  | 	((float *)buffer)[1] = right_value; | ||
|  | } | ||
|  | 
 | ||
|  | static OSStatus appIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, | ||
|  |                           AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *userdata) { | ||
|  | 	affirm(AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &deviceFormat)); | ||
|  | 	if (samples_per_second != (int)deviceFormat.mSampleRate) { | ||
|  | 		samples_per_second = (int)deviceFormat.mSampleRate; | ||
|  | 		kinc_a2_internal_sample_rate_callback(); | ||
|  | 	} | ||
|  | 	int num_frames = deviceBufferSize / deviceFormat.mBytesPerFrame; | ||
|  | 	kinc_a2_internal_callback(&a2_buffer, num_frames); | ||
|  | 	float *output = (float *)outOutputData->mBuffers[0].mData; | ||
|  | 	for (int i = 0; i < num_frames; ++i) { | ||
|  | 		copySample(output); | ||
|  | 		output += 2; | ||
|  | 	} | ||
|  | 	return kAudioHardwareNoError; | ||
|  | } | ||
|  | 
 | ||
|  | 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)); | ||
|  | 
 | ||
|  | 	device = kAudioDeviceUnknown; | ||
|  | 
 | ||
|  | 	initialized = false; | ||
|  | 
 | ||
|  | 	size = sizeof(AudioDeviceID); | ||
|  | 	address.mSelector = kAudioHardwarePropertyDefaultOutputDevice; | ||
|  | 	address.mScope = kAudioObjectPropertyScopeGlobal; | ||
|  | 	address.mElement = kAudioObjectPropertyElementMaster; | ||
|  | 	affirm(AudioObjectGetPropertyData(kAudioObjectSystemObject, &address, 0, NULL, &size, &device)); | ||
|  | 
 | ||
|  | 	size = sizeof(UInt32); | ||
|  | 	address.mSelector = kAudioDevicePropertyBufferSize; | ||
|  | 	address.mScope = kAudioDevicePropertyScopeOutput; | ||
|  | 	affirm(AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &deviceBufferSize)); | ||
|  | 
 | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "deviceBufferSize = %i\n", deviceBufferSize); | ||
|  | 
 | ||
|  | 	size = sizeof(AudioStreamBasicDescription); | ||
|  | 	address.mSelector = kAudioDevicePropertyStreamFormat; | ||
|  | 	address.mScope = kAudioDevicePropertyScopeOutput; | ||
|  | 
 | ||
|  | 	affirm(AudioObjectGetPropertyData(device, &address, 0, NULL, &size, &deviceFormat)); | ||
|  | 
 | ||
|  | 	if (deviceFormat.mFormatID != kAudioFormatLinearPCM) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "mFormatID !=  kAudioFormatLinearPCM\n"); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (!(deviceFormat.mFormatFlags & kLinearPCMFormatFlagIsFloat)) { | ||
|  | 		kinc_log(KINC_LOG_LEVEL_ERROR, "Only works with float format.\n"); | ||
|  | 		return; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if (samples_per_second != (int)deviceFormat.mSampleRate) { | ||
|  | 		samples_per_second = (int)deviceFormat.mSampleRate; | ||
|  | 		kinc_a2_internal_sample_rate_callback(); | ||
|  | 	} | ||
|  | 
 | ||
|  | 	initialized = true; | ||
|  | 
 | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "mSampleRate = %g\n", deviceFormat.mSampleRate); | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "mFormatFlags = %08X\n", (unsigned int)deviceFormat.mFormatFlags); | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "mBytesPerPacket = %d\n", (unsigned int)deviceFormat.mBytesPerPacket); | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "mFramesPerPacket = %d\n", (unsigned int)deviceFormat.mFramesPerPacket); | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "mChannelsPerFrame = %d\n", (unsigned int)deviceFormat.mChannelsPerFrame); | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "mBytesPerFrame = %d\n", (unsigned int)deviceFormat.mBytesPerFrame); | ||
|  | 	kinc_log(KINC_LOG_LEVEL_INFO, "mBitsPerChannel = %d\n", (unsigned int)deviceFormat.mBitsPerChannel); | ||
|  | 
 | ||
|  | 	if (soundPlaying) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	affirm(AudioDeviceCreateIOProcID(device, appIOProc, NULL, &theIOProcID)); | ||
|  | 	affirm(AudioDeviceStart(device, theIOProcID)); | ||
|  | 
 | ||
|  | 	soundPlaying = true; | ||
|  | } | ||
|  | 
 | ||
|  | void kinc_a2_update(void) {} | ||
|  | 
 | ||
|  | void kinc_a2_shutdown(void) { | ||
|  | 	if (!initialized) | ||
|  | 		return; | ||
|  | 	if (!soundPlaying) | ||
|  | 		return; | ||
|  | 
 | ||
|  | 	affirm(AudioDeviceStop(device, theIOProcID)); | ||
|  | 	affirm(AudioDeviceDestroyIOProcID(device, theIOProcID)); | ||
|  | 
 | ||
|  | 	soundPlaying = false; | ||
|  | } |