⛏️ index : haiku.git

/*
 * Copyright 2007 Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Bek, host.haiku@gmx.de
 */
#include "driver.h"


status_t
null_hw_create_virtual_buffers(device_stream_t* stream, const char* name)
{
	uint32 i;
	int buffer_size;
	int area_size;
	uint8* buffer;

	buffer_size = stream->num_channels
				* format_to_sample_size(stream->format)
				* stream->buffer_length;
	buffer_size = (buffer_size + 127) & (~127);

	area_size = buffer_size * stream->num_buffers;
	area_size = (area_size + B_PAGE_SIZE - 1) & (~(B_PAGE_SIZE -1));

	stream->buffer_area = create_area("null_audio_buffers", (void**)&buffer,
							B_ANY_KERNEL_ADDRESS, area_size,
							B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
	if (stream->buffer_area < B_OK)
		return stream->buffer_area;

	// Get the correct address for setting up the buffers
	// pointers being passed back to userland
	for (i = 0; i < stream->num_buffers; i++)
		stream->buffers[i] = buffer + (i * buffer_size);

	stream->buffer_ready_sem = create_sem(0, name);
	return B_OK;
}


static int32
null_fake_interrupt(void* cookie)
{
	// This thread is supposed to fake the interrupt
	// handling done in communication with the
	// hardware usually. What it does is nearly the
	// same like all soundrivers, get the interrupt
	// exchange the buffer pointer and update the
	// time information. Instead of exiting, we wait
	// until the next fake interrupt appears.
	bigtime_t sleepTime;
	device_t* device = (device_t*) cookie;
	int sampleRate;

	switch (device->playback_stream.rate) {
		case B_SR_48000:
			sampleRate = 48000;
			break;
		case B_SR_44100:
		default:
			sampleRate = 44100;
			break;
	}

	// The time between until we get a new valid buffer
	// from our soundcard: buffer_length / samplerate
	sleepTime = (device->playback_stream.buffer_length * 1000000LL) / sampleRate;

	while (device->running) {
		cpu_status status;
		status = disable_interrupts();
		acquire_spinlock(&device->playback_stream.lock);
		device->playback_stream.real_time = system_time();
		device->playback_stream.frames_count += device->playback_stream.buffer_length;
		device->playback_stream.buffer_cycle = (device->playback_stream.buffer_cycle +1) % device->playback_stream.num_buffers;
		release_spinlock(&device->playback_stream.lock);

		// TODO: Create a simple sinus wave, so that recording from
		// the virtual device actually returns something useful
		acquire_spinlock(&device->record_stream.lock);
		device->record_stream.real_time = device->playback_stream.real_time;
		device->record_stream.frames_count += device->record_stream.buffer_length;
		device->record_stream.buffer_cycle = (device->record_stream.buffer_cycle +1) % device->record_stream.num_buffers;
		release_spinlock(&device->record_stream.lock);

		restore_interrupts(status);

		release_sem_etc(device->playback_stream.buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
		release_sem_etc(device->record_stream.buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
		snooze(sleepTime);
	}
	return B_OK;
}


status_t
null_start_hardware(device_t* device)
{
	dprintf("null_audio: %s spawning fake interrupter\n", __func__);
	device->running = true;
	device->interrupt_thread = spawn_kernel_thread(null_fake_interrupt, "null_audio interrupter",
								B_REAL_TIME_PRIORITY, (void*)device);
	return resume_thread(device->interrupt_thread);
}


void
null_stop_hardware(device_t* device)
{
	device->running = false;
}