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 "NstResourceString.hpp"
26 #include "NstIoScreen.hpp"
27 #include "NstWindowParam.hpp"
28 #include "NstWindowUser.hpp"
29 #include "NstManager.hpp"
30 #include "NstDialogSound.hpp"
31 #include "NstManagerSound.hpp"
32 #include "NstManagerSoundRecorder.hpp"
33 
34 namespace Nestopia
35 {
36 	namespace Managers
37 	{
Recorder(Window::Menu & m,Window::Sound::Recorder & d,Emulator & e)38 		Sound::Recorder::Recorder
39 		(
40 			Window::Menu& m,
41 			Window::Sound::Recorder& d,
42 			Emulator& e
43 		)
44 		:
45 		Manager   ( e, m, this, &Recorder::OnEmuEvent ),
46 		recording ( false ),
47 		file      ( Io::Wave::MODE_WRITE ),
48 		dialog    ( d )
49 		{
50 			static const Window::Menu::CmdHandler::Entry<Recorder> commands[] =
51 			{
52 				{ IDM_FILE_SOUND_RECORDER_FILE,   &Recorder::OnCmdFile   },
53 				{ IDM_FILE_SOUND_RECORDER_START,  &Recorder::OnCmdRecord },
54 				{ IDM_FILE_SOUND_RECORDER_STOP,   &Recorder::OnCmdStop   },
55 				{ IDM_FILE_SOUND_RECORDER_REWIND, &Recorder::OnCmdRewind }
56 			};
57 
58 			menu.Commands().Add( this, commands );
59 
60 			static const Window::Menu::PopupHandler::Entry<Recorder> popups[] =
61 			{
62 				{ Window::Menu::PopupHandler::Pos<IDM_POS_FILE,IDM_POS_FILE_SOUNDRECORDER>::ID, &Recorder::OnMenu }
63 			};
64 
65 			menu.Popups().Add( this, popups );
66 		}
67 
~Recorder()68 		Sound::Recorder::~Recorder()
69 		{
70 		}
71 
CanRecord() const72 		bool Sound::Recorder::CanRecord() const
73 		{
74 			return
75 			(
76 				!recording &&
77 				dialog.WaveFile().Length() &&
78 				waveFormat.nSamplesPerSec &&
79 				waveFormat.wBitsPerSample &&
80 				!emulator.NetPlayers() &&
81 				emulator.IsOn()
82 			);
83 		}
84 
CanRewind() const85 		bool Sound::Recorder::CanRewind() const
86 		{
87 			return !recording && file.IsOpen();
88 		}
89 
CanStop() const90 		bool Sound::Recorder::CanStop() const
91 		{
92 			return recording;
93 		}
94 
Close()95 		void Sound::Recorder::Close()
96 		{
97 			recording = false;
98 
99 			try
100 			{
101 				file.Close();
102 			}
103 			catch (Io::Wave::Exception id)
104 			{
105 				Window::User::Fail( id );
106 			}
107 		}
108 
Enable(const WAVEFORMATEX * newWaveFormat)109 		void Sound::Recorder::Enable(const WAVEFORMATEX* newWaveFormat)
110 		{
111 			if (newWaveFormat)
112 			{
113 				if (waveFormat == *newWaveFormat)
114 					return;
115 
116 				waveFormat = *newWaveFormat;
117 			}
118 			else
119 			{
120 				waveFormat.Clear();
121 			}
122 
123 			Close();
124 		}
125 
OnMenu(const Window::Menu::PopupHandler::Param & param)126 		void Sound::Recorder::OnMenu(const Window::Menu::PopupHandler::Param& param)
127 		{
128 			menu[ IDM_FILE_SOUND_RECORDER_START  ].Enable( !param.show || CanRecord() );
129 			menu[ IDM_FILE_SOUND_RECORDER_STOP   ].Enable( !param.show || CanStop()   );
130 			menu[ IDM_FILE_SOUND_RECORDER_REWIND ].Enable( !param.show || CanRewind() );
131 		}
132 
OnEmuEvent(const Emulator::Event event,const Emulator::Data data)133 		void Sound::Recorder::OnEmuEvent(const Emulator::Event event,const Emulator::Data data)
134 		{
135 			switch (event)
136 			{
137 				case Emulator::EVENT_POWER_OFF:
138 
139 					recording = false;
140 					break;
141 
142 				case Emulator::EVENT_NETPLAY_MODE:
143 
144 					menu[IDM_POS_FILE][IDM_POS_FILE_SOUNDRECORDER].Enable( !data );
145 					break;
146 			}
147 		}
148 
OnCmdFile(uint)149 		void Sound::Recorder::OnCmdFile(uint)
150 		{
151 			dialog.Open();
152 
153 			if (file.IsOpen() && file.GetName() != dialog.WaveFile())
154 				Close();
155 		}
156 
OnCmdRecord(uint)157 		void Sound::Recorder::OnCmdRecord(uint)
158 		{
159 			if (CanRecord())
160 			{
161 				if (!file.IsOpen())
162 				{
163 					try
164 					{
165 						file.Open( dialog.WaveFile(), waveFormat );
166 					}
167 					catch (Io::Wave::Exception id)
168 					{
169 						Window::User::Fail( id );
170 						return;
171 					}
172 
173 					size = 0;
174 					nextSmallSizeNotification = SMALL_SIZE;
175 					nextBigSizeNotification = BIG_SIZE;
176 				}
177 
178 				Io::Screen() << Resource::String(size ? IDS_SCREEN_SOUND_RECORDER_RESUME : IDS_SCREEN_SOUND_RECORDER_START);
179 
180 				recording = true;
181 				Resume();
182 			}
183 		}
184 
OnCmdStop(uint)185 		void Sound::Recorder::OnCmdStop(uint)
186 		{
187 			if (CanStop())
188 			{
189 				Io::Screen() << Resource::String(IDS_SCREEN_SOUND_RECORDER_STOP);
190 
191 				recording = false;
192 				Resume();
193 			}
194 		}
195 
OnCmdRewind(uint)196 		void Sound::Recorder::OnCmdRewind(uint)
197 		{
198 			if (CanRewind())
199 			{
200 				try
201 				{
202 					file.Close();
203 				}
204 				catch (Io::Wave::Exception id)
205 				{
206 					Window::User::Fail( id );
207 				}
208 
209 				Resume();
210 			}
211 		}
212 
Flush(const Nes::Sound::Output & output)213 		void Sound::Recorder::Flush(const Nes::Sound::Output& output)
214 		{
215 			if (recording)
216 			{
217 				NST_VERIFY( file.IsOpen() );
218 
219 				try
220 				{
221 					for (uint i=0; i < 2; ++i)
222 						file.Write( output.samples[i], output.length[i] * waveFormat.nBlockAlign );
223 				}
224 				catch (Io::Wave::Exception ids)
225 				{
226 					recording = false;
227 					Window::User::Fail( ids );
228 					return;
229 				}
230 
231 				size += output.length[0] + output.length[1];
232 
233 				if (size >= nextBigSizeNotification)
234 				{
235 					nextBigSizeNotification += BIG_SIZE;
236 					Window::User::Inform( IDS_WAVE_WARN_FILE_BIG );
237 				}
238 				else if (size >= nextSmallSizeNotification)
239 				{
240 					Io::Screen() << Resource::String(IDS_SCREEN_SOUND_RECORDER_WRITTEN).Invoke( HeapString() << (nextSmallSizeNotification / ONE_MB) );
241 					nextSmallSizeNotification += SMALL_SIZE;
242 				}
243 			}
244 		}
245 	}
246 }
247