1 ////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // Nestopia - NES/Famicom emulator written in C++
4 //
5 // Copyright (C) 2003-2008 Martin Freij
6 //
7 // This file is part of Nestopia.
8 //
9 // Nestopia is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // Nestopia is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with Nestopia; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 ////////////////////////////////////////////////////////////////////////////////////////
24 
25 #include <new>
26 #include "NstCpu.hpp"
27 #include "NstChips.hpp"
28 #include "NstSoundPlayer.hpp"
29 #include "api/NstApiUser.hpp"
30 
31 namespace Nes
32 {
33 	namespace Core
34 	{
35 		namespace Sound
36 		{
37 			#ifdef NST_MSVC_OPTIMIZE
38 			#pragma optimize("s", on)
39 			#endif
40 
Slot()41 			Player::Slot::Slot()
42 			: data(NULL) {}
43 
~Slot()44 			Player::Slot::~Slot()
45 			{
46 				delete [] data;
47 			}
48 
Player(Apu & a,uint n)49 			Player::Player(Apu& a,uint n)
50 			: Pcm(a), slots(new Slot [n]), numSlots(n)
51 			{
52 				NST_ASSERT( n );
53 			}
54 
~Player()55 			Player::~Player()
56 			{
57 				delete [] slots;
58 			}
59 
Create(Apu & apu,const Chips & chips,wcstring const chip,Game game,uint maxSamples)60 			Player* Player::Create(Apu& apu,const Chips& chips,wcstring const chip,Game game,uint maxSamples)
61 			{
62 				if (!maxSamples)
63 					return NULL;
64 
65 				if (chip && chips.Has(chip) && chips[chip].HasSamples())
66 				{
67 					game = GAME_UNKNOWN;
68 				}
69 				else if (game != GAME_UNKNOWN)
70 				{
71 					maxSamples = uint(game) >> GAME_NUM_SAMPLES_SHIFT;
72 					NST_ASSERT( maxSamples );
73 				}
74 				else
75 				{
76 					return NULL;
77 				}
78 
79 				if (Player* const player = new (std::nothrow) Player(apu,maxSamples))
80 				{
81 					for (uint i=0; i < maxSamples; ++i)
82 					{
83 						class Loader : public Api::User::File
84 						{
85 							const Action action;
86 							Slot& slot;
87 							const uint id;
88 							wcstring const filename;
89 
90 							Action GetAction() const throw()
91 							{
92 								return action;
93 							}
94 
95 							wcstring GetName() const throw()
96 							{
97 								return filename;
98 							}
99 
100 							uint GetId() const throw()
101 							{
102 								return id;
103 							}
104 
105 							Result SetSampleContent(const void* data,ulong length,bool stereo,uint bits,ulong rate) throw()
106 							{
107 								if (!data || !length)
108 									return RESULT_ERR_INVALID_PARAM;
109 
110 								if (!Pcm::CanDo( bits, rate ))
111 									return RESULT_ERR_UNSUPPORTED;
112 
113 								iword* NST_RESTRICT dst = new (std::nothrow) iword [length];
114 
115 								if (!dst)
116 									return RESULT_ERR_OUT_OF_MEMORY;
117 
118 								slot.data = dst;
119 								slot.length = length;
120 								slot.rate = rate;
121 
122 								if (bits == 8)
123 								{
124 									const byte* NST_RESTRICT src = static_cast<const byte*>(data);
125 									const byte* const end = src + length;
126 
127 									if (stereo)
128 									{
129 										for (; src != end; src += 2)
130 										{
131 											const idword sample = (idword(uint(src[0]) << 8) - 32768) + (idword(uint(src[1]) << 8) - 32768);
132 											*dst++ = Clamp<Apu::Channel::OUTPUT_MIN,Apu::Channel::OUTPUT_MAX>(sample);
133 										}
134 									}
135 									else
136 									{
137 										for (; src != end; src += 1)
138 										{
139 											const idword sample = idword(uint(*src) << 8) - 32768;
140 											*dst++ = Clamp<Apu::Channel::OUTPUT_MIN,Apu::Channel::OUTPUT_MAX>(sample);
141 										}
142 									}
143 								}
144 								else
145 								{
146 									const iword* NST_RESTRICT src = static_cast<const iword*>(data);
147 									const iword* const end = src + length;
148 
149 									if (stereo)
150 									{
151 										for (; src != end; src += 2)
152 										{
153 											const idword sample = src[0] + src[1];
154 											*dst++ = Clamp<Apu::Channel::OUTPUT_MIN,Apu::Channel::OUTPUT_MAX>(sample);
155 										}
156 									}
157 									else
158 									{
159 										for (; src != end; src += 1)
160 										{
161 											const idword sample = *src;
162 											*dst++ = Clamp<Apu::Channel::OUTPUT_MIN,Apu::Channel::OUTPUT_MAX>(sample);
163 										}
164 									}
165 								}
166 
167 								return RESULT_OK;
168 							}
169 
170 						public:
171 
172 							Loader(Game g,Slot& s,uint i,wcstring f)
173 							:
174 							action
175 							(
176 								g == GAME_MOERO_PRO_YAKYUU         ? LOAD_SAMPLE_MOERO_PRO_YAKYUU :
177 								g == GAME_MOERO_PRO_YAKYUU_88      ? LOAD_SAMPLE_MOERO_PRO_YAKYUU_88 :
178 								g == GAME_MOERO_PRO_TENNIS         ? LOAD_SAMPLE_MOERO_PRO_TENNIS :
179 								g == GAME_TERAO_NO_DOSUKOI_OOZUMOU ? LOAD_SAMPLE_TERAO_NO_DOSUKOI_OOZUMOU :
180 								g == GAME_AEROBICS_STUDIO          ? LOAD_SAMPLE_AEROBICS_STUDIO :
181                                                                      LOAD_SAMPLE
182 							),
183 							slot     (s),
184 							id       (i),
185 							filename (f)
186 							{
187 							}
188 						};
189 
190 						wcstring filename = L"";
191 
192 						if (game != GAME_UNKNOWN || *(filename = *chips[chip].Sample(i)))
193 						{
194 							Loader loader( game, player->slots[i], i, filename );
195 
196 							try
197 							{
198 								Api::User::fileIoCallback( loader );
199 							}
200 							catch (...)
201 							{
202 								delete player;
203 								throw;
204 							}
205 						}
206 					}
207 
208 					for (uint i=0; i < maxSamples; ++i)
209 					{
210 						if (player->slots[i].data)
211 							return player;
212 					}
213 
214 					delete player;
215 				}
216 
217 				return NULL;
218 			}
219 
Destroy(Player * player)220 			void Player::Destroy(Player* player)
221 			{
222 				delete player;
223 			}
224 
225 			#ifdef NST_MSVC_OPTIMIZE
226 			#pragma optimize("", on)
227 			#endif
228 		}
229 	}
230 }
231