1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include <pspthreadman.h>
24 #include <pspaudio.h>
25 
26 #include "common/scummsys.h"
27 #include "backends/platform/psp/audio.h"
28 
29 //#define __PSP_DEBUG_FUNCS__	/* For debugging function calls */
30 //#define __PSP_DEBUG_PRINT__	/* For debug printouts */
31 
32 #include "backends/platform/psp/trace.h"
33 
open(uint32 freq,uint32 numOfChannels,uint32 numOfSamples,callbackFunc callback,void * userData)34 bool PspAudio::open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, callbackFunc callback, void *userData) {
35 	DEBUG_ENTER_FUNC();
36 	if (_init) {
37 		PSP_ERROR("audio device already initialized\n");
38 		return true;
39 	}
40 
41 	PSP_DEBUG_PRINT("freq[%d], numOfChannels[%d], numOfSamples[%d], callback[%p], userData[%x]\n",
42 			freq, numOfChannels, numOfSamples, callback, (uint32)userData);
43 
44 	numOfSamples = PSP_AUDIO_SAMPLE_ALIGN(numOfSamples);
45 	uint32 bufLen = numOfSamples * numOfChannels * NUM_BUFFERS * sizeof(uint16);
46 
47 	PSP_DEBUG_PRINT("total buffer size[%d]\n", bufLen);
48 
49 	_buffers[0] = (byte *)memalign(64, bufLen);
50 	if (!_buffers[0]) {
51 		PSP_ERROR("failed to allocate memory for audio buffers\n");
52 		return false;
53 	}
54 	memset(_buffers[0], 0, bufLen);	// clean the buffer
55 
56 	// Fill in the rest of the buffer pointers
57 	byte *pBuffer = _buffers[0];
58 	for (int i = 1; i < NUM_BUFFERS; i++) {
59 		pBuffer += numOfSamples * numOfChannels * sizeof(uint16);
60 		_buffers[i] = pBuffer;
61 	}
62 
63 	// Reserve a HW channel for our audio
64 	_pspChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, numOfSamples, numOfChannels == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO);
65 	if (_pspChannel < 0) {
66 		PSP_ERROR("failed to reserve audio channel\n");
67 		return false;
68 	}
69 
70 	PSP_DEBUG_PRINT("reserved channel[%d] for audio\n", _pspChannel);
71 
72 	// Save our data
73 	_numOfChannels = numOfChannels;
74 	_numOfSamples = numOfSamples;
75 	_bufferSize = numOfSamples * numOfChannels * sizeof(uint16);	// should be the right size to send the app
76 	_callback = callback;
77 	_userData = userData;
78 	_bufferToFill = 0;
79 	_bufferToPlay = 0;
80 
81 	_init = true;
82 	_paused = true;	// start in paused mode
83 
84 	threadCreateAndStart("audioThread", PRIORITY_AUDIO_THREAD, STACK_AUDIO_THREAD);	// start the consumer thread
85 
86 	return true;
87 }
88 
89 // The real thread function
threadFunction()90 void PspAudio::threadFunction() {
91 	assert(_callback);
92 	PSP_DEBUG_PRINT_FUNC("audio thread started\n");
93 
94 	while (_init) {		// Keep looping so long as we haven't been told to stop
95 		if (_paused)
96 			PSP_DEBUG_PRINT("audio thread paused\n");
97 		while (_paused) {	// delay until we stop pausing
98 			PspThread::delayMicros(100000);	// 100ms
99 			if (!_paused)
100 				PSP_DEBUG_PRINT("audio thread unpaused\n");
101 		}
102 
103 		PSP_DEBUG_PRINT("filling buffer[%d]\n", _bufferToFill);
104 		_callback(_userData, _buffers[_bufferToFill], _bufferSize); // ask mixer to fill in data
105 		nextBuffer(_bufferToFill);
106 
107 		PSP_DEBUG_PRINT("playing buffer[%d].\n", _bufferToPlay);
108 		playBuffer();
109 		nextBuffer(_bufferToPlay);
110 	} // while _init
111 
112 	// destroy everything
113 	free(_buffers[0]);
114 	sceAudioChRelease(_pspChannel);
115 	PSP_DEBUG_PRINT("audio thread exiting. ****************************\n");
116 }
117 
118 // Much faster than using %, especially with conditional moves (MIPS)
nextBuffer(int & bufferIdx)119 inline void PspAudio::nextBuffer(int &bufferIdx) {
120 	DEBUG_ENTER_FUNC();
121 	bufferIdx++;
122 	if (bufferIdx >= NUM_BUFFERS)
123 		bufferIdx = 0;
124 }
125 
126 // Don't do it with blocking
playBuffer()127 inline bool PspAudio::playBuffer() {
128 	DEBUG_ENTER_FUNC();
129 	int ret;
130 	if (_numOfChannels == 1)
131 		ret = sceAudioOutputBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]);
132 	else
133 		ret = sceAudioOutputPannedBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]);
134 
135 	if (ret < 0) {
136 		PSP_ERROR("failed to output audio. Error[%d]\n", ret);
137 		return false;
138 	}
139 	return true;
140 }
141 
close()142 void PspAudio::close() {
143 	PSP_DEBUG_PRINT("close has been called ***************\n");
144 	_init = false;
145 }
146