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