xref: /haiku/src/apps/soundrecorder/RecorderWindow.cpp (revision 7d6915b4d08ffe728cd38af02843d5e98ddfe0db)
1 /*
2  * Copyright 2014, Dario Casalinuovo. All rights reserved.
3  * Copyright 2005, Jérôme Duval. All rights reserved.
4  * Distributed under the terms of the MIT License.
5  *
6  * Inspired by SoundCapture from Be newsletter (Media Kit Basics:
7  *	Consumers and Producers)
8  */
9 
10 #include <Application.h>
11 #include <Alert.h>
12 #include <Debug.h>
13 #include <Screen.h>
14 #include <Button.h>
15 #include <CheckBox.h>
16 #include <TextControl.h>
17 #include <MenuField.h>
18 #include <PopUpMenu.h>
19 #include <MenuItem.h>
20 #include <Box.h>
21 #include <ScrollView.h>
22 #include <Beep.h>
23 #include <StringView.h>
24 #include <String.h>
25 #include <Slider.h>
26 #include <Message.h>
27 
28 #include <Path.h>
29 #include <FindDirectory.h>
30 #include <MediaAddOn.h>
31 
32 #include <SoundPlayer.h>
33 
34 #include <assert.h>
35 #include <stdio.h>
36 #include <strings.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 
42 #include <MediaRoster.h>
43 #include <TimeSource.h>
44 #include <NodeInfo.h>
45 
46 #include "RecorderWindow.h"
47 #include "FileUtils.h"
48 
49 #if ! NDEBUG
50 #define FPRINTF(args) fprintf args
51 #else
52 #define FPRINTF(args)
53 #endif
54 
55 #define DEATH FPRINTF
56 #define CONNECT FPRINTF
57 #define WINDOW FPRINTF
58 
59 #undef B_TRANSLATION_CONTEXT
60 #define B_TRANSLATION_CONTEXT "RecorderWindow"
61 
62 
63 // default window positioning
64 static const float MIN_WIDTH = 400.0f;
65 static const float MIN_HEIGHT = 175.0f;
66 static const float XPOS = 100.0f;
67 static const float YPOS = 200.0f;
68 
69 #define FOURCC(a,b,c,d)	((((uint32)(d)) << 24) | (((uint32)(c)) << 16) \
70 	| (((uint32)(b)) << 8) | ((uint32)(a)))
71 
72 struct riff_struct
73 {
74 	uint32 riff_id; // 'RIFF'
75 	uint32 len;
76 	uint32 wave_id;	// 'WAVE'
77 };
78 
79 struct chunk_struct
80 {
81 	uint32 fourcc;
82 	uint32 len;
83 };
84 
85 struct format_struct
86 {
87 	uint16 format_tag;
88 	uint16 channels;
89 	uint32 samples_per_sec;
90 	uint32 avg_bytes_per_sec;
91 	uint16 block_align;
92 	uint16 bits_per_sample;
93 };
94 
95 
96 struct wave_struct
97 {
98 	struct riff_struct riff;
99 	struct chunk_struct format_chunk;
100 	struct format_struct format;
101 	struct chunk_struct data_chunk;
102 };
103 
104 
105 RecorderWindow::RecorderWindow()
106 	:
107 	BWindow(BRect(XPOS, YPOS, XPOS + MIN_WIDTH, YPOS + MIN_HEIGHT),
108 		B_TRANSLATE_SYSTEM_NAME("SoundRecorder"), B_TITLED_WINDOW,
109 		B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE),
110 	fPlayer(NULL),
111 	fSoundList(NULL),
112 	fPlayFile(NULL),
113 	fPlayTrack(NULL),
114 	fPlayFrames(0),
115 	fLooping(false),
116 	fSavePanel(NULL),
117 	fInitCheck(B_OK)
118 {
119 	fRoster = NULL;
120 	fRecordButton = NULL;
121 	fPlayButton = NULL;
122 	fStopButton = NULL;
123 	fSaveButton = NULL;
124 	fLoopButton = NULL;
125 	fInputField = NULL;
126 	fRecorder = NULL;
127 	fRecording = false;
128 	fTempCount = -1;
129 	fButtonState = btnPaused;
130 
131 	CalcSizes(MIN_WIDTH, MIN_HEIGHT);
132 
133 	fInitCheck = InitWindow();
134 	if (fInitCheck != B_OK) {
135 		if (fInitCheck == B_NAME_NOT_FOUND)
136 			ErrorAlert(B_TRANSLATE("Cannot find default audio hardware"),
137 				fInitCheck);
138 		else
139 			ErrorAlert(B_TRANSLATE("Cannot connect to media server"),
140 				fInitCheck);
141 		PostMessage(B_QUIT_REQUESTED);
142 	} else
143 		Show();
144 }
145 
146 
147 RecorderWindow::~RecorderWindow()
148 {
149 	//  The MediaRecorder have to be deleted, the dtor
150 	//  disconnect it from the media_kit.
151 	delete fRecorder;
152 
153 	delete fPlayer;
154 
155 	if (fPlayTrack && fPlayFile)
156 		fPlayFile->ReleaseTrack(fPlayTrack);
157 
158 	if (fPlayFile)
159 		delete fPlayFile;
160 	fPlayTrack = NULL;
161 	fPlayFile = NULL;
162 
163 	//	Clean up items in list view.
164 	if (fSoundList) {
165 		fSoundList->DeselectAll();
166 		for (int i = 0; i < fSoundList->CountItems(); i++) {
167 			WINDOW((stderr, "clean up item %d\n", i+1));
168 			SoundListItem* item = dynamic_cast<SoundListItem *>(fSoundList->ItemAt(i));
169 			if (item) {
170 				if (item->IsTemp())
171 					item->Entry().Remove();	//	delete temp file
172 				delete item;
173 			}
174 		}
175 		fSoundList->MakeEmpty();
176 	}
177 	//	Clean up currently recording file, if any.
178 	fRecEntry.Remove();
179 	fRecEntry.Unset();
180 
181 	delete fSavePanel;
182 }
183 
184 
185 status_t
186 RecorderWindow::InitCheck()
187 {
188 	return fInitCheck;
189 }
190 
191 
192 void
193 RecorderWindow::CalcSizes(float min_width, float min_height)
194 {
195 	//	Set up size limits based on new screen size
196 	BScreen screen(this);
197 	BRect rect = screen.Frame();
198 	float width = rect.Width() - 12;
199 	SetSizeLimits(min_width, width, min_height, rect.Height() - 24);
200 
201 	//	Don't zoom to cover all of screen; user can resize last bit if necessary.
202 	//	This leaves other windows visible.
203 	if (width > 640)
204 		width = 640 + (width - 640) / 2;
205 	SetZoomLimits(width, rect.Height() - 24);
206 }
207 
208 
209 status_t
210 RecorderWindow::InitWindow()
211 {
212 	BPopUpMenu * popup = 0;
213 	status_t error;
214 
215 	try {
216 		//	Find temp directory for recorded sounds.
217 		BPath path;
218 		if (!(error = find_directory(B_SYSTEM_TEMP_DIRECTORY, &path)))
219 			error = fTempDir.SetTo(path.Path());
220 		if (error < 0)
221 			goto bad_mojo;
222 
223 		//	Make sure the media roster is there (which means the server is there).
224 		fRoster = BMediaRoster::Roster(&error);
225 		if (!fRoster)
226 			goto bad_mojo;
227 
228 		error = fRoster->GetAudioInput(&fAudioInputNode);
229 		if (error < B_OK) //	there's no input?
230 			goto bad_mojo;
231 
232 		error = fRoster->GetAudioMixer(&fAudioMixerNode);
233 		if (error < B_OK) //	there's no mixer?
234 			goto bad_mojo;
235 
236 		fRecorder = new BMediaRecorder("Sound Recorder",
237 			B_MEDIA_RAW_AUDIO);
238 
239 		if (fRecorder->InitCheck() < B_OK)
240 			goto bad_mojo;
241 
242 		// Set the node to accept only audio data
243 		media_format output_format;
244 		output_format.type = B_MEDIA_RAW_AUDIO;
245 		output_format.u.raw_audio = media_raw_audio_format::wildcard;
246 		fRecorder->SetAcceptedFormat(output_format);
247 
248 		//	Create the window header with controls
249 		BRect r(Bounds());
250 		r.bottom = r.top + 175;
251 		BBox *background = new BBox(r, "_background",
252 			B_FOLLOW_LEFT_RIGHT	| B_FOLLOW_TOP, B_WILL_DRAW
253 			| B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
254 
255 		AddChild(background);
256 
257 		r = background->Bounds();
258 		r.left = 0;
259 		r.right = r.left + 38;
260 		r.bottom = r.top + 104;
261 		fVUView = new VUView(r, B_FOLLOW_LEFT|B_FOLLOW_TOP);
262 		background->AddChild(fVUView);
263 
264 		r = background->Bounds();
265 		r.left = r.left + 40;
266 		r.bottom = r.top + 104;
267 		fScopeView = new ScopeView(r, B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP);
268 		background->AddChild(fScopeView);
269 
270 		r = background->Bounds();
271 		r.left = 2;
272 		r.right -= 26;
273 		r.top = 115;
274 		r.bottom = r.top + 30;
275 		fTrackSlider = new TrackSlider(r, "trackSlider", new BMessage(POSITION_CHANGED),
276 			B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
277 		background->AddChild(fTrackSlider);
278 
279 		BRect buttonRect;
280 
281 		//	Button for rewinding
282 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
283 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-7, 25));
284 		fRewindButton = new TransportButton(buttonRect, B_TRANSLATE("Rewind"),
285 			kSkipBackBitmapBits, kPressedSkipBackBitmapBits,
286 			kDisabledSkipBackBitmapBits, new BMessage(REWIND));
287 		background->AddChild(fRewindButton);
288 
289 		//	Button for stopping recording or playback
290 		buttonRect = BRect(BPoint(0,0), kStopButtonSize);
291 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-48, 25));
292 		fStopButton = new TransportButton(buttonRect, B_TRANSLATE("Stop"),
293 			kStopButtonBitmapBits, kPressedStopButtonBitmapBits,
294 			kDisabledStopButtonBitmapBits, new BMessage(STOP));
295 		background->AddChild(fStopButton);
296 
297 		//	Button for starting playback of selected sound
298 		BRect playRect(BPoint(0,0), kPlayButtonSize);
299 		playRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-82, 25));
300 		fPlayButton = new PlayPauseButton(playRect, B_TRANSLATE("Play"),
301 			new BMessage(PLAY), new BMessage(PLAY_PERIOD), ' ', 0);
302 		background->AddChild(fPlayButton);
303 
304 		//	Button for forwarding
305 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
306 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-133, 25));
307 		fForwardButton = new TransportButton(buttonRect, B_TRANSLATE("Forward"),
308 			kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits,
309 			kDisabledSkipForwardBitmapBits, new BMessage(FORWARD));
310 		background->AddChild(fForwardButton);
311 
312 		//	Button to start recording (or waiting for sound)
313 		buttonRect = BRect(BPoint(0,0), kRecordButtonSize);
314 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-174, 25));
315 		fRecordButton = new RecordButton(buttonRect, B_TRANSLATE("Record"),
316 			new BMessage(RECORD), new BMessage(RECORD_PERIOD));
317 		background->AddChild(fRecordButton);
318 
319 		//	Button for saving selected sound
320 		buttonRect = BRect(BPoint(0,0), kDiskButtonSize);
321 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-250, 21));
322 		fSaveButton = new TransportButton(buttonRect, B_TRANSLATE("Save"),
323 			kDiskButtonBitmapsBits, kPressedDiskButtonBitmapsBits,
324 			kDisabledDiskButtonBitmapsBits, new BMessage(SAVE));
325 		fSaveButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
326 		background->AddChild(fSaveButton);
327 
328 		//	Button Loop
329 		buttonRect = BRect(BPoint(0,0), kArrowSize);
330 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(23, 48));
331 		fLoopButton = new DrawButton(buttonRect, B_TRANSLATE("Loop"),
332 			kLoopArrowBits, kArrowBits, new BMessage(LOOP));
333 		fLoopButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
334 		fLoopButton->SetTarget(this);
335 		background->AddChild(fLoopButton);
336 
337 		buttonRect = BRect(BPoint(0,0), kSpeakerIconBitmapSize);
338 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(121, 17));
339 		SpeakerView *speakerView = new SpeakerView(buttonRect,
340 			B_FOLLOW_LEFT | B_FOLLOW_TOP);
341 		speakerView->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
342 		background->AddChild(speakerView);
343 
344 		buttonRect = BRect(BPoint(0,0), BPoint(84, 19));
345 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(107, 20));
346 		fVolumeSlider = new VolumeSlider(buttonRect, "volumeSlider",
347 			B_FOLLOW_LEFT | B_FOLLOW_TOP);
348 		fVolumeSlider->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
349 		background->AddChild(fVolumeSlider);
350 
351 		// Button to mask/see sounds list
352 		buttonRect = BRect(BPoint(0,0), kUpDownButtonSize);
353 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(21, 25));
354 		fUpDownButton = new UpDownButton(buttonRect, new BMessage(VIEW_LIST));
355 		fUpDownButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
356 		background->AddChild(fUpDownButton);
357 
358 		r = Bounds();
359 		r.top = background->Bounds().bottom + 1;
360 		fBottomBox = new BBox(r, "bottomBox", B_FOLLOW_ALL);
361 		fBottomBox->SetBorder(B_NO_BORDER);
362 		AddChild(fBottomBox);
363 
364 		//	The actual list of recorded sounds (initially empty) sits
365 		//	below the header with the controls.
366 		r = fBottomBox->Bounds();
367 		r.left += 190;
368 		r.InsetBy(10, 10);
369 		r.left -= 10;
370 		r.top += 4;
371 		r.right -= B_V_SCROLL_BAR_WIDTH;
372 		r.bottom -= 25;
373 		fSoundList = new SoundListView(r, B_TRANSLATE("Sound List"),
374 			B_FOLLOW_ALL);
375 		fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED));
376 		fSoundList->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
377 		BScrollView *scroller = new BScrollView("scroller", fSoundList,
378 			B_FOLLOW_ALL, 0, false, true, B_FANCY_BORDER);
379 		fBottomBox->AddChild(scroller);
380 
381 		r = fBottomBox->Bounds();
382 		r.right = r.left + 190;
383 		r.bottom -= 25;
384 		r.InsetBy(10, 8);
385 		r.top -= 1;
386 		fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT);
387 		fFileInfoBox->SetLabel(B_TRANSLATE("File info"));
388 
389 		fFileInfoBox->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
390 
391 		BFont font = be_plain_font;
392 		font.SetSize(font.Size() * 0.92f);
393 		font_height height;
394 		font.GetHeight(&height);
395 		float fontHeight = height.ascent + height.leading + height.descent;
396 
397 		r = fFileInfoBox->Bounds();
398 		r.left = 8;
399 		r.top = fontHeight + 6;
400 		r.bottom = r.top + fontHeight + 3;
401 		r.right -= 10;
402 		fFilename = new BStringView(r, "filename", B_TRANSLATE("File name:"));
403 		fFileInfoBox->AddChild(fFilename);
404 		fFilename->SetFont(&font, B_FONT_SIZE);
405 		fFilename->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
406 
407 		r.top += fontHeight;
408 		r.bottom = r.top + fontHeight + 3;
409 		fFormat = new BStringView(r, "format", B_TRANSLATE("Format:"));
410 		fFileInfoBox->AddChild(fFormat);
411 		fFormat->SetFont(&font, B_FONT_SIZE);
412 		fFormat->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
413 
414 		r.top += fontHeight;
415 		r.bottom = r.top + fontHeight + 3;
416 		fCompression = new BStringView(r, "compression",
417 			B_TRANSLATE("Compression:"));
418 		fFileInfoBox->AddChild(fCompression);
419 		fCompression->SetFont(&font, B_FONT_SIZE);
420 		fCompression->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
421 
422 		r.top += fontHeight;
423 		r.bottom = r.top + fontHeight + 3;
424 		fChannels = new BStringView(r, "channels", B_TRANSLATE("Channels:"));
425 		fFileInfoBox->AddChild(fChannels);
426 		fChannels->SetFont(&font, B_FONT_SIZE);
427 		fChannels->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
428 
429 		r.top += fontHeight;
430 		r.bottom = r.top + fontHeight + 3;
431 		fSampleSize = new BStringView(r, "samplesize",
432 			B_TRANSLATE("Sample size:"));
433 		fFileInfoBox->AddChild(fSampleSize);
434 		fSampleSize->SetFont(&font, B_FONT_SIZE);
435 		fSampleSize->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
436 
437 		r.top += fontHeight;
438 		r.bottom = r.top + fontHeight + 3;
439 		fSampleRate = new BStringView(r, "samplerate",
440 			B_TRANSLATE("Sample rate:"));
441 		fFileInfoBox->AddChild(fSampleRate);
442 		fSampleRate->SetFont(&font, B_FONT_SIZE);
443 		fSampleRate->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
444 
445 		r.top += fontHeight;
446 		r.bottom = r.top + fontHeight + 3;
447 		fDuration = new BStringView(r, "duration", B_TRANSLATE("Duration:"));
448 		fFileInfoBox->AddChild(fDuration);
449 		fDuration->SetFont(&font, B_FONT_SIZE);
450 		fDuration->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
451 
452 		fFileInfoBox->ResizeTo(fFileInfoBox->Frame().Width(),
453 			r.bottom + fontHeight / 2.0f);
454 		fDeployedHeight = MIN_HEIGHT + fFileInfoBox->Bounds().Height() + 40.0f;
455 
456 		//	Input selection lists all available physical inputs that produce
457 		//	buffers with B_MEDIA_RAW_AUDIO format data.
458 		popup = new BPopUpMenu(B_TRANSLATE("Input"));
459 		const int maxInputCount = 64;
460 		dormant_node_info dni[maxInputCount];
461 
462 		int32 real_count = maxInputCount;
463 
464 		error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format,
465 			0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT);
466 		if (real_count > maxInputCount) {
467 			WINDOW((stderr, "dropped %" B_PRId32 " inputs\n", real_count - maxInputCount));
468 			real_count = maxInputCount;
469 		}
470 		char selected_name[B_MEDIA_NAME_LENGTH] = "Default input";
471 		BMessage * msg;
472 		BMenuItem * item;
473 		for (int i = 0; i < real_count; i++) {
474 			msg = new BMessage(INPUT_SELECTED);
475 			msg->AddData("node", B_RAW_TYPE, &dni[i], sizeof(dni[i]));
476 			item = new BMenuItem(dni[i].name, msg);
477 			popup->AddItem(item);
478 			media_node_id ni[12];
479 			int32 ni_count = 12;
480 			error = fRoster->GetInstancesFor(dni[i].addon, dni[i].flavor_id,
481 				ni, &ni_count);
482 			if (error == B_OK) {
483 				for (int j = 0; j < ni_count; j++) {
484 					if (ni[j] == fAudioInputNode.node) {
485 						strcpy(selected_name, dni[i].name);
486 						break;
487 					}
488 				}
489 			}
490 		}
491 
492 		//	Create the actual widget
493 		r = fFileInfoBox->Bounds();
494 		r.top = r.bottom + 2;
495 		r.bottom = r.top + 18;
496 		r.InsetBy(10, 10);
497 		fInputField = new BMenuField(r, "Input", B_TRANSLATE("Input:"), popup);
498 		fInputField->SetDivider(fInputField->StringWidth(B_TRANSLATE("Input:"))
499 			+ 4.0f);
500 		fBottomBox->AddChild(fInputField);
501 
502 		fBottomBox->AddChild(fFileInfoBox);
503 
504 		fBottomBox->Hide();
505 		CalcSizes(MIN_WIDTH, MIN_HEIGHT);
506 		ResizeTo(Frame().Width(), MIN_HEIGHT);
507 
508 		popup->Superitem()->SetLabel(selected_name);
509 
510 		// Make sure the save panel is happy.
511 		fSavePanel = new BFilePanel(B_SAVE_PANEL);
512 		fSavePanel->SetTarget(this);
513 	}
514 	catch (...) {
515 		goto bad_mojo;
516 	}
517 	UpdateButtons();
518 	return B_OK;
519 
520 	//	Error handling.
521 bad_mojo:
522 	if (error >= 0)
523 		error = B_ERROR;
524 	if (fRecorder)
525 		delete fRecorder;
526 
527 	delete fPlayer;
528 	if (!fInputField)
529 		delete popup;
530 	return error;
531 }
532 
533 
534 bool
535 RecorderWindow::QuitRequested()	//	this means Close pressed
536 {
537 	StopRecording();
538 	StopPlaying();
539 	be_app->PostMessage(B_QUIT_REQUESTED);
540 	return true;
541 }
542 
543 
544 void
545 RecorderWindow::MessageReceived(BMessage * message)
546 {
547 	//	Your average generic message dispatching switch() statement.
548 	switch (message->what) {
549 	case INPUT_SELECTED:
550 		Input(message);
551 		break;
552 	case SOUND_SELECTED:
553 		Selected(message);
554 		break;
555 	case STOP_PLAYING:
556 		StopPlaying();
557 		break;
558 	case STOP_RECORDING:
559 		StopRecording();
560 		break;
561 	case PLAY_PERIOD:
562 		if (fPlayer) {
563 			if (fPlayer->HasData())
564 				fPlayButton->SetPlaying();
565 			else
566 				fPlayButton->SetPaused();
567 		}
568 		break;
569 	case RECORD_PERIOD:
570 		fRecordButton->SetRecording();
571 		break;
572 	case RECORD:
573 		Record(message);
574 		break;
575 	case STOP:
576 		Stop(message);
577 		break;
578 	case PLAY:
579 		Play(message);
580 		break;
581 	case SAVE:
582 		Save(message);
583 		break;
584 	case B_SAVE_REQUESTED:
585 		DoSave(message);
586 		break;
587 	case VIEW_LIST:
588 		if (fUpDownButton->Value() == B_CONTROL_ON) {
589 			fBottomBox->Show();
590 			CalcSizes(MIN_WIDTH, fDeployedHeight);
591 			ResizeTo(Frame().Width(), fDeployedHeight);
592 		} else {
593 			fBottomBox->Hide();
594 			CalcSizes(MIN_WIDTH, MIN_HEIGHT);
595 			ResizeTo(Frame().Width(), MIN_HEIGHT);
596 
597 		}
598 		break;
599 	case UPDATE_TRACKSLIDER:
600 		{
601 			bigtime_t timestamp = fPlayTrack->CurrentTime();
602 			fTrackSlider->SetMainTime(timestamp, false);
603 			fScopeView->SetMainTime(timestamp);
604 		}
605 		break;
606 	case POSITION_CHANGED:
607 		{
608 			bigtime_t right, left, main;
609 			if (message->FindInt64("main", &main) == B_OK) {
610 				if (fPlayTrack) {
611 					fPlayTrack->SeekToTime(fTrackSlider->MainTime());
612 					fPlayFrame = fPlayTrack->CurrentFrame();
613 				}
614 				fScopeView->SetMainTime(main);
615 			}
616 			if (message->FindInt64("right", &right) == B_OK) {
617 				if (fPlayTrack) {
618 					fPlayLimit = MIN(fPlayFrames,
619 						(off_t)(right * fPlayFormat.u.raw_audio.frame_rate
620 							/ 1000000LL));
621 				}
622 				fScopeView->SetRightTime(right);
623 			}
624 			if (message->FindInt64("left", &left) == B_OK)
625 				fScopeView->SetLeftTime(left);
626 			break;
627 		}
628 	case LOOP:
629 		fLooping = fLoopButton->ButtonState();
630 		break;
631 	case B_SIMPLE_DATA:
632 	case B_REFS_RECEIVED:
633 		{
634 			RefsReceived(message);
635 			break;
636 		}
637 	case B_COPY_TARGET:
638 		CopyTarget(message);
639 		break;
640 	default:
641 		BWindow::MessageReceived(message);
642 		break;
643 	}
644 }
645 
646 
647 void
648 RecorderWindow::Record(BMessage * message)
649 {
650 	//	User pressed Record button
651 	fRecording = true;
652 	if (fButtonState != btnPaused) {
653 		StopRecording();
654 		return;			//	user is too fast on the mouse
655 	}
656 	SetButtonState(btnRecording);
657 	fRecordButton->SetRecording();
658 
659 	char name[256];
660 	//	Create a file with a temporary name
661 	status_t err = NewTempName(name);
662 	if (err < B_OK) {
663 		ErrorAlert(B_TRANSLATE("Cannot find an unused name to use for the "
664 			"new recording"), err);
665 		return;
666 	}
667 	//	Find the file so we can refer to it later
668 	err = fTempDir.FindEntry(name, &fRecEntry);
669 	if (err < B_OK) {
670 		ErrorAlert(B_TRANSLATE("Cannot find the temporary file created to "
671 			"hold the new recording"), err);
672 		return;
673 	}
674 	err = fRecFile.SetTo(&fTempDir, name, O_RDWR);
675 	if (err < B_OK) {
676 		ErrorAlert(B_TRANSLATE("Cannot open the temporary file created to "
677 			"hold the new recording"), err);
678 		fRecEntry.Unset();
679 		return;
680 	}
681 	//	Reserve space on disk (creates fewer fragments)
682 	err = fRecFile.SetSize(4 * fRecordFormat.u.raw_audio.channel_count
683 		* fRecordFormat.u.raw_audio.frame_rate
684 		* (fRecordFormat.u.raw_audio.format
685 			& media_raw_audio_format::B_AUDIO_SIZE_MASK));
686 	if (err < B_OK) {
687 		ErrorAlert(B_TRANSLATE("Cannot record a sound that long"), err);
688 		fRecEntry.Remove();
689 		fRecEntry.Unset();
690 		return;
691 	}
692 	fRecSize = 0;
693 
694 	fRecFile.Seek(sizeof(struct wave_struct), SEEK_SET);
695 
696 	//	Hook up input
697 	err = MakeRecordConnection(fAudioInputNode);
698 	if (err < B_OK) {
699 		ErrorAlert(B_TRANSLATE("Cannot connect to the selected sound input"),
700 			err);
701 		fRecEntry.Remove();
702 		fRecEntry.Unset();
703 		return;
704 	}
705 
706 	fRecorder->Start();
707 }
708 
709 
710 void
711 RecorderWindow::Play(BMessage * message)
712 {
713 	if (fPlayer) {
714 		//	User pressed Play button and playing
715 		if (fPlayer->HasData())
716 			fPlayButton->SetPaused();
717 		else
718 			fPlayButton->SetPlaying();
719 		fPlayer->SetHasData(!fPlayer->HasData());
720 		return;
721 	}
722 
723 	SetButtonState(btnPlaying);
724 	fPlayButton->SetPlaying();
725 
726 	if (!fPlayTrack) {
727 		ErrorAlert(B_TRANSLATE("Cannot get the file to play"), B_ERROR);
728 		return;
729 	}
730 
731 	fPlayLimit = MIN(fPlayFrames, (off_t)(fTrackSlider->RightTime()
732 		* fPlayFormat.u.raw_audio.frame_rate / 1000000LL));
733 	fPlayTrack->SeekToTime(fTrackSlider->MainTime());
734 	fPlayFrame = fPlayTrack->CurrentFrame();
735 
736 	// Create our internal Node which plays sound, and register it.
737 	fPlayer = new BSoundPlayer(fAudioMixerNode, &fPlayFormat.u.raw_audio,
738 		"Sound Player");
739 	status_t err = fPlayer->InitCheck();
740 	if (err < B_OK)
741 		return;
742 
743 	fVolumeSlider->SetSoundPlayer(fPlayer);
744 	fPlayer->SetCallbacks(PlayFile, NotifyPlayFile, this);
745 
746 	//	And get it going...
747 	fPlayer->Start();
748 	fPlayer->SetHasData(true);
749 }
750 
751 
752 void
753 RecorderWindow::Stop(BMessage * message)
754 {
755 	//	User pressed Stop button.
756 	//	Stop recorder.
757 	StopRecording();
758 	//	Stop player.
759 	StopPlaying();
760 }
761 
762 
763 void
764 RecorderWindow::Save(BMessage * message)
765 {
766 	//	User pressed Save button.
767 	//	Find the item to save.
768 	int32 index = fSoundList->CurrentSelection();
769 	SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(index));
770 	if ((! pItem) || (pItem->Entry().InitCheck() != B_OK))
771 		return;
772 
773 	// Update the save panel and show it.
774 	char filename[B_FILE_NAME_LENGTH];
775 	pItem->Entry().GetName(filename);
776 	BMessage saveMsg(B_SAVE_REQUESTED);
777 	entry_ref ref;
778 	pItem->Entry().GetRef(&ref);
779 
780 	if (saveMsg.AddPointer("sound list item", pItem) != B_OK)
781 		fprintf(stderr, "failed to add pItem\n");
782 	fSavePanel->SetSaveText(filename);
783 	fSavePanel->SetMessage(&saveMsg);
784 	fSavePanel->Show();
785 }
786 
787 
788 void
789 RecorderWindow::DoSave(BMessage * message)
790 {
791 	// User picked a place to put the file.
792 	// Find the location of the old (e.g.
793 	// temporary file), and the name of the
794 	// new file to save.
795 	entry_ref old_ref, new_dir_ref;
796 	const char* new_name;
797 	SoundListItem* pItem;
798 
799 	if ((message->FindPointer("sound list item", (void**) &pItem) == B_OK)
800 		&& (message->FindRef("directory", &new_dir_ref) == B_OK)
801 		&& (message->FindString("name", &new_name) == B_OK)) {
802 		BEntry& oldEntry = pItem->Entry();
803 		BFile oldFile(&oldEntry, B_READ_WRITE);
804 		if (oldFile.InitCheck() != B_OK)
805 			return;
806 
807 		BDirectory newDir(&new_dir_ref);
808 		if (newDir.InitCheck() != B_OK)
809 			return;
810 
811 		BFile newFile;
812 		newDir.CreateFile(new_name, &newFile);
813 
814 		if (newFile.InitCheck() != B_OK)
815 			return;
816 
817 		status_t err = CopyFile(newFile, oldFile);
818 
819 		if (err == B_OK) {
820 			// clean up the sound list and item
821 			if (pItem->IsTemp())
822 				oldEntry.Remove(); // blows away temp file!
823 			oldEntry.SetTo(&newDir, new_name);
824 			pItem->SetTemp(false);	// don't blow the new entry away when we exit!
825 			fSoundList->Invalidate();
826 		}
827 	} else {
828 		WINDOW((stderr, "Couldn't save file.\n"));
829 	}
830 }
831 
832 
833 void
834 RecorderWindow::Input(BMessage * message)
835 {
836 	//	User selected input from pop-up
837 	const dormant_node_info * dni = 0;
838 	ssize_t size = 0;
839 	if (message->FindData("node", B_RAW_TYPE, (const void **)&dni, &size))
840 		return;		//	bad input selection message
841 
842 	media_node_id node_id;
843 	status_t error = fRoster->GetInstancesFor(dni->addon, dni->flavor_id, &node_id);
844 	if (error != B_OK)
845 		fRoster->InstantiateDormantNode(*dni, &fAudioInputNode);
846 	else
847 		fRoster->GetNodeFor(node_id, &fAudioInputNode);
848 }
849 
850 
851 void
852 RecorderWindow::Selected(BMessage * message)
853 {
854 	//	User selected a sound in list view
855 	int32 selIdx = fSoundList->CurrentSelection();
856 	SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(selIdx));
857 	if (!pItem)
858 		return;
859 	status_t err = UpdatePlayFile(pItem, true);
860 	if (err != B_OK) {
861 		ErrorAlert(B_TRANSLATE("Cannot recognize this file as a media file"),
862 			err == B_MEDIA_NO_HANDLER ? B_OK : err);
863 		RemoveCurrentSoundItem();
864 	}
865 	UpdateButtons();
866 }
867 
868 
869 status_t
870 RecorderWindow::MakeRecordConnection(const media_node & input)
871 {
872 	CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n"));
873 
874 	status_t err = B_OK;
875 	media_output audioOutput;
876 
877 	if (!fRecorder->IsConnected()) {
878 		//	Find an available output for the given input node.
879 		int32 count = 0;
880 		err = fRoster->GetFreeOutputsFor(input, &audioOutput, 1,
881 			&count, B_MEDIA_RAW_AUDIO);
882 
883 		if (err < B_OK) {
884 			CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
885 				" couldn't get free outputs from audio input node\n"));
886 			return err;
887 		}
888 
889 		if (count < 1) {
890 			CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
891 				" no free outputs from audio input node\n"));
892 			return B_BUSY;
893 		}
894 
895 	} else {
896 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
897 			" audio input node already connected\n"));
898 
899 		return B_BUSY;
900 	}
901 
902 	//	Get a format, any format.
903 	fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio;
904 	fRecordFormat.type = B_MEDIA_RAW_AUDIO;
905 
906 	//	Tell the consumer where we want data to go.
907 	err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this);
908 
909 	if (err < B_OK) {
910 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
911 			" couldn't set the sound recorder's hook functions\n"));
912 		return err;
913 	}
914 
915 	if (!fRecorder->IsConnected()) {
916 
917 		err = fRecorder->Connect(input, &audioOutput, &fRecordFormat);
918 
919 		if (err < B_OK) {
920 			CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
921 				" failed to connect sound recorder to audio input node.\n"));
922 
923 			fRecorder->SetHooks(NULL, NULL, NULL);
924 			return err;
925 		}
926 	}
927 
928 	return B_OK;
929 }
930 
931 
932 status_t
933 RecorderWindow::BreakRecordConnection()
934 {
935 	status_t err = B_OK;
936 
937 	err = fRecorder->Stop(true);
938 	if (err < B_OK)
939 		return err;
940 
941 	return fRecorder->Disconnect();
942 }
943 
944 
945 status_t
946 RecorderWindow::StopRecording()
947 {
948 	if (!fRecording)
949 		return B_OK;
950 	fRecording = false;
951 
952 	BreakRecordConnection();
953 
954 	fRecorder->SetHooks(NULL, NULL, NULL);
955 
956 	if (fRecSize > 0) {
957 
958 		wave_struct header;
959 		header.riff.riff_id = FOURCC('R','I','F','F');
960 		header.riff.len = fRecSize + sizeof(header) - 8;
961 		header.riff.wave_id = FOURCC('W','A','V','E');
962 		header.format_chunk.fourcc = FOURCC('f','m','t',' ');
963 		header.format_chunk.len = sizeof(header.format);
964 		header.format.format_tag = 1;
965 		header.format.channels = fRecordFormat.u.raw_audio.channel_count;
966 		header.format.samples_per_sec = (uint32)fRecordFormat.u.raw_audio.frame_rate;
967 		header.format.avg_bytes_per_sec = (uint32)(fRecordFormat.u.raw_audio.frame_rate
968 			* fRecordFormat.u.raw_audio.channel_count
969 			* (fRecordFormat.u.raw_audio.format & 0xf));
970 		header.format.bits_per_sample = (fRecordFormat.u.raw_audio.format & 0xf) * 8;
971 		header.format.block_align = (fRecordFormat.u.raw_audio.format & 0xf)
972 			* fRecordFormat.u.raw_audio.channel_count;
973 		header.data_chunk.fourcc = FOURCC('d','a','t','a');
974 		header.data_chunk.len = fRecSize;
975 		fRecFile.Seek(0, SEEK_SET);
976 		fRecFile.Write(&header, sizeof(header));
977 
978 		fRecFile.SetSize(fRecSize + sizeof(header));
979 		//	We reserve space; make sure we cut off any excess at the end.
980 		AddSoundItem(fRecEntry, true);
981 	} else
982 		fRecEntry.Remove();
983 
984 	//	We're done for this time.
985 	fRecEntry.Unset();
986 	//	Close the file.
987 	fRecFile.Unset();
988 	//	No more recording going on.
989 	fRecSize = 0;
990 	SetButtonState(btnPaused);
991 	fRecordButton->SetStopped();
992 
993 	return B_OK;
994 }
995 
996 
997 status_t
998 RecorderWindow::StopPlaying()
999 {
1000 	if (fPlayer) {
1001 		fPlayer->Stop();
1002 		fPlayer->SetCallbacks(0, 0, 0);
1003 		fVolumeSlider->SetSoundPlayer(NULL);
1004 		delete fPlayer;
1005 		fPlayer = NULL;
1006 	}
1007 	SetButtonState(btnPaused);
1008 	fPlayButton->SetStopped();
1009 	fTrackSlider->ResetMainTime();
1010 	fScopeView->SetMainTime(*fTrackSlider->MainTime());
1011 	return B_OK;
1012 }
1013 
1014 
1015 void
1016 RecorderWindow::SetButtonState(BtnState state)
1017 {
1018 	fButtonState = state;
1019 	UpdateButtons();
1020 }
1021 
1022 
1023 void
1024 RecorderWindow::UpdateButtons()
1025 {
1026 	bool hasSelection = (fSoundList->CurrentSelection() >= 0);
1027 	fRecordButton->SetEnabled(fButtonState != btnPlaying);
1028 	fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
1029 	fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
1030 	fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
1031 	fStopButton->SetEnabled(fButtonState != btnPaused);
1032 	fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording));
1033 	fInputField->SetEnabled(fButtonState != btnRecording);
1034 }
1035 
1036 #ifndef __HAIKU__
1037 extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(
1038 	BMediaTrack *self, media_format *inout_format);
1039 #endif
1040 
1041 
1042 status_t
1043 RecorderWindow::UpdatePlayFile(SoundListItem* item, bool updateDisplay)
1044 {
1045 	fScopeView->CancelRendering();
1046 	StopPlaying();
1047 	StopRecording();
1048 
1049 	if (fPlayTrack && fPlayFile) {
1050 		fPlayFile->ReleaseTrack(fPlayTrack);
1051 		fPlayTrack = NULL;
1052 	}
1053 	if (fPlayFile) {
1054 		delete fPlayFile;
1055 		fPlayFile = NULL;
1056 	}
1057 
1058 	status_t err;
1059 	BEntry& entry = item->Entry();
1060 	entry_ref ref;
1061 	entry.GetRef(&ref);
1062 	fPlayFile = new BMediaFile(&ref); //, B_MEDIA_FILE_UNBUFFERED);
1063 	if ((err = fPlayFile->InitCheck()) < B_OK) {
1064 		delete fPlayFile;
1065 		fPlayFile = NULL;
1066 		return err;
1067 	}
1068 
1069 	for (int ix=0; ix < fPlayFile->CountTracks(); ix++) {
1070 		BMediaTrack * track = fPlayFile->TrackAt(ix);
1071 		fPlayFormat.type = B_MEDIA_RAW_AUDIO;
1072 #ifdef __HAIKU__
1073 		if ((track->DecodedFormat(&fPlayFormat) == B_OK)
1074 #else
1075 		if ((DecodedFormat__11BMediaTrackP12media_format(track, &fPlayFormat) == B_OK)
1076 #endif
1077 			&& (fPlayFormat.type == B_MEDIA_RAW_AUDIO)) {
1078 			fPlayTrack = track;
1079 			break;
1080 		}
1081 		if (track)
1082 			fPlayFile->ReleaseTrack(track);
1083 	}
1084 
1085 	if (!fPlayTrack) {
1086 		delete fPlayFile;
1087 		fPlayFile = NULL;
1088 		return B_STREAM_NOT_FOUND;
1089 	}
1090 
1091 	if (!updateDisplay)
1092 		return B_OK;
1093 
1094 	BString filename = B_TRANSLATE("File name: ");
1095 	filename << ref.name;
1096 	fFilename->SetText(filename.String());
1097 
1098 	BString format = B_TRANSLATE("Format: ");
1099 	media_file_format file_format;
1100 	if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK)
1101 		format << file_format.short_name;
1102 	BString compression = B_TRANSLATE("Compression: ");
1103 	media_codec_info codec_info;
1104 	if (fPlayTrack->GetCodecInfo(&codec_info) == B_OK) {
1105 		if (strcmp(codec_info.short_name, "raw")==0)
1106 			compression << B_TRANSLATE("None");
1107 		else
1108 			compression << codec_info.short_name;
1109 	}
1110 	BString channels = B_TRANSLATE("Channels: ");
1111 	channels << fPlayFormat.u.raw_audio.channel_count;
1112 	BString samplesize = B_TRANSLATE("Sample size: ");
1113 	samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf)
1114 		<< B_TRANSLATE(" bits");
1115 	BString samplerate = B_TRANSLATE("Sample rate: ");
1116 	samplerate << (int)fPlayFormat.u.raw_audio.frame_rate;
1117 	BString durationString = B_TRANSLATE("Duration: ");
1118 	bigtime_t duration = fPlayTrack->Duration();
1119 	durationString << (float)(duration / 1000000.0) << B_TRANSLATE(" seconds");
1120 
1121 	fFormat->SetText(format.String());
1122 	fCompression->SetText(compression.String());
1123 	fChannels->SetText(channels.String());
1124 	fSampleSize->SetText(samplesize.String());
1125 	fSampleRate->SetText(samplerate.String());
1126 	fDuration->SetText(durationString.String());
1127 
1128 	fTrackSlider->SetTotalTime(duration, true);
1129 	fScopeView->SetTotalTime(duration, true);
1130 	fScopeView->RenderTrack(fPlayTrack, fPlayFormat);
1131 
1132 	fPlayFrames = fPlayTrack->CountFrames();
1133 	return B_OK;
1134 }
1135 
1136 
1137 void
1138 RecorderWindow::ErrorAlert(const char * action, status_t err)
1139 {
1140 	char msg[300];
1141 	if (err != B_OK)
1142 		sprintf(msg, "%s: %s. [%" B_PRIx32 "]", action, strerror(err), (int32) err);
1143 	else
1144 		sprintf(msg, "%s.", action);
1145 	BAlert* alert = new BAlert("", msg, B_TRANSLATE("Stop"));
1146 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1147 	alert->Go();
1148 }
1149 
1150 
1151 status_t
1152 RecorderWindow::NewTempName(char * name)
1153 {
1154 	int init_count = fTempCount;
1155 again:
1156 	if (fTempCount-init_count > 25) {
1157 		return B_ERROR;
1158 	}
1159 	else {
1160 		fTempCount++;
1161 		if (fTempCount==0)
1162 			sprintf(name, "Audio Clip");
1163 		else
1164 			sprintf(name, "Audio Clip %d", fTempCount);
1165 		BPath path;
1166 		status_t err;
1167 		BEntry tempEnt;
1168 		if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) {
1169 			return err;
1170 		}
1171 		if ((err = tempEnt.GetPath(&path)) < B_OK) {
1172 			return err;
1173 		}
1174 		path.Append(name);
1175 		int fd;
1176 		//	Use O_EXCL so we know we created the file (sync with other instances)
1177 		if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
1178 			goto again;
1179 		}
1180 		close(fd);
1181 	}
1182 	return B_OK;
1183 }
1184 
1185 
1186 void
1187 RecorderWindow::AddSoundItem(const BEntry& entry, bool temp)
1188 {
1189 	//	Create list item to display.
1190 	SoundListItem * listItem = new SoundListItem(entry, temp);
1191 	fSoundList->AddItem(listItem);
1192 	fSoundList->Invalidate();
1193 	fSoundList->Select(fSoundList->IndexOf(listItem));
1194 }
1195 
1196 
1197 void
1198 RecorderWindow::RemoveCurrentSoundItem() {
1199 	int32 index = fSoundList->CurrentSelection();
1200 	BListItem *item = fSoundList->RemoveItem(index);
1201 	delete item;
1202 	if (index >= fSoundList->CountItems())
1203 		index = fSoundList->CountItems() - 1;
1204 	fSoundList->Select(index);
1205 }
1206 
1207 
1208 void
1209 RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,
1210 	void* data, size_t size, const media_format &format)
1211 {
1212 	//	Callback called from the SoundConsumer when receiving buffers.
1213 	RecorderWindow * window = (RecorderWindow *)cookie;
1214 
1215 	if (window->fRecording) {
1216 		//	Write the data to file (we don't buffer or guard file access
1217 		//	or anything)
1218 		window->fRecFile.WriteAt(window->fRecSize, data, size);
1219 		window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format);
1220 		window->fRecSize += size;
1221 	}
1222 }
1223 
1224 
1225 void
1226 RecorderWindow::NotifyRecordFile(void * cookie,
1227 	BMediaRecorder::notification code, ...)
1228 {
1229 	if (code == BMediaRecorder::B_WILL_STOP) {
1230 		RecorderWindow * window = (RecorderWindow *)cookie;
1231 		// Tell the window we've stopped, if it doesn't
1232 		// already know.
1233 		window->PostMessage(STOP_RECORDING);
1234 	}
1235 }
1236 
1237 
1238 void
1239 RecorderWindow::PlayFile(void * cookie, void * data, size_t size,
1240 	const media_raw_audio_format & format)
1241 {
1242 	//	Callback called from the SoundProducer when producing buffers.
1243 	RecorderWindow * window = (RecorderWindow *)cookie;
1244 	int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) *
1245 		window->fPlayFormat.u.raw_audio.channel_count;
1246 
1247 	if ((window->fPlayFrame < window->fPlayLimit) || window->fLooping) {
1248 		if (window->fPlayFrame >= window->fPlayLimit) {
1249 			bigtime_t left = window->fTrackSlider->LeftTime();
1250 			window->fPlayTrack->SeekToTime(&left);
1251 			window->fPlayFrame = window->fPlayTrack->CurrentFrame();
1252 		}
1253 		int64 frames = 0;
1254 		window->fPlayTrack->ReadFrames(data, &frames);
1255 		window->fVUView->ComputeLevels(data, size / frame_size, format.format);
1256 		window->fPlayFrame += size/frame_size;
1257 		window->PostMessage(UPDATE_TRACKSLIDER);
1258 	} else {
1259 		//	we're done!
1260 		window->PostMessage(STOP_PLAYING);
1261 	}
1262 }
1263 
1264 
1265 void
1266 RecorderWindow::NotifyPlayFile(void * cookie,
1267 	BSoundPlayer::sound_player_notification code, ...)
1268 {
1269 	if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) {
1270 		RecorderWindow * window = (RecorderWindow *)cookie;
1271 		// tell the window we've stopped, if it doesn't
1272 		// already know.
1273 		window->PostMessage(STOP_PLAYING);
1274 	}
1275 }
1276 
1277 
1278 void
1279 RecorderWindow::RefsReceived(BMessage *msg)
1280 {
1281 	entry_ref ref;
1282 	int32 i = 0;
1283 	int32 countGood = 0;
1284 	int32 countBad = 0;
1285 
1286 	while (msg->FindRef("refs", i++, &ref) == B_OK) {
1287 
1288 		BEntry entry(&ref, true);
1289 		BPath path(&entry);
1290 		BNode node(&entry);
1291 
1292 		if (node.IsFile()) {
1293 			SoundListItem * listItem = new SoundListItem(entry, false);
1294 			if (UpdatePlayFile(listItem) == B_OK) {
1295 				fSoundList->AddItem(listItem);
1296 				countGood++;
1297 				continue;
1298 			}
1299 			delete listItem;
1300 		} else if(node.IsDirectory()) {
1301 
1302 		}
1303 		countBad++;
1304 	}
1305 
1306 	if (countBad == 1 && countGood == 0) {
1307 		BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1308 			B_TRANSLATE("The file doesn't appear to be an audio file."),
1309 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1310 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1311 		alert->Go();
1312 	} else if (countBad > 0 && countGood == 0) {
1313 		BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1314 			B_TRANSLATE("None of the files appear to be audio files."),
1315 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1316 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1317 		alert->Go();
1318 	} else if (countGood > 0) {
1319 		if (countBad > 0) {
1320 			BAlert* alert = new BAlert(B_TRANSLATE("Invalid audio files"),
1321 			B_TRANSLATE("Some of the files don't appear to be audio files."),
1322 				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
1323 				B_WARNING_ALERT);
1324 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1325 			alert->Go();
1326 		}
1327 		fSoundList->Select(fSoundList->CountItems() - 1);
1328 	}
1329 }
1330 
1331 
1332 void
1333 RecorderWindow::CopyTarget(BMessage *msg)
1334 {
1335 	const char *type = NULL;
1336 	if (msg->FindString("be:types", &type) == B_OK) {
1337 		if (!strcasecmp(type, B_FILE_MIME_TYPE)) {
1338 			const char *name;
1339 			entry_ref dir;
1340 			if (msg->FindString("be:filetypes") == B_OK
1341 				&& msg->FindString("name", &name) == B_OK
1342 				&& msg->FindRef("directory", &dir) == B_OK) {
1343 				BDirectory directory(&dir);
1344 				BFile file(&directory, name, O_RDWR | O_TRUNC);
1345 
1346 				// seek time
1347 				bigtime_t start = fTrackSlider->LeftTime();
1348 
1349 				// write data
1350 				bigtime_t diffTime = fTrackSlider->RightTime()
1351 					- fTrackSlider->LeftTime();
1352 				int64 framesToWrite = (int64) (diffTime
1353 					* fPlayFormat.u.raw_audio.frame_rate / 1000000LL);
1354 				int32 frameSize = (fPlayFormat.u.raw_audio.format & 0xf)
1355 					* fPlayFormat.u.raw_audio.channel_count;
1356 
1357 				wave_struct header;
1358 				header.riff.riff_id = FOURCC('R','I','F','F');
1359 				header.riff.len
1360 					= (frameSize * framesToWrite) + sizeof(header) - 8;
1361 				header.riff.wave_id = FOURCC('W','A','V','E');
1362 				header.format_chunk.fourcc = FOURCC('f','m','t',' ');
1363 				header.format_chunk.len = sizeof(header.format);
1364 				header.format.format_tag = 1;
1365 				header.format.channels = fPlayFormat.u.raw_audio.channel_count;
1366 				header.format.samples_per_sec
1367 					= (uint32)fPlayFormat.u.raw_audio.frame_rate;
1368 				header.format.avg_bytes_per_sec
1369 					= (uint32)(fPlayFormat.u.raw_audio.frame_rate
1370 					* fPlayFormat.u.raw_audio.channel_count
1371 					* (fPlayFormat.u.raw_audio.format & 0xf));
1372 				header.format.bits_per_sample
1373 					= (fPlayFormat.u.raw_audio.format & 0xf) * 8;
1374 				header.format.block_align = frameSize;
1375 				header.data_chunk.fourcc = FOURCC('d','a','t','a');
1376 				header.data_chunk.len = frameSize * framesToWrite;
1377 				file.Seek(0, SEEK_SET);
1378 				file.Write(&header, sizeof(header));
1379 
1380 				char *data = (char *)malloc(fPlayFormat.u.raw_audio.buffer_size);
1381 
1382 				fPlayTrack->SeekToTime(&start);
1383 				fPlayFrame = fPlayTrack->CurrentFrame();
1384 				while (framesToWrite > 0) {
1385 					int64 frames = 0;
1386 					status_t err = fPlayTrack->ReadFrames(data, &frames);
1387 					if (frames <= 0 || err != B_OK) {
1388 						if (err != B_OK)
1389 							fprintf(stderr, "CopyTarget: ReadFrames failed\n");
1390 						break;
1391 					}
1392 					file.Write(data, frames * frameSize);
1393 					framesToWrite -= frames;
1394 				}
1395 
1396 				file.Sync();
1397 				free(data);
1398 				BNodeInfo nodeInfo(&file);
1399 				// set type
1400 			}
1401 		} else {
1402 
1403 		}
1404 	}
1405 }
1406