xref: /haiku/src/apps/soundrecorder/RecorderWindow.cpp (revision f7fceb6920c6de6f515c69d1cf1636486adb9dbc)
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 
RecorderWindow()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 	fExternalConnection = false;
129 	fTempCount = -1;
130 	fButtonState = btnPaused;
131 
132 	CalcSizes(MIN_WIDTH, MIN_HEIGHT);
133 
134 	fInitCheck = InitWindow();
135 	if (fInitCheck != B_OK) {
136 		if (fInitCheck == B_NAME_NOT_FOUND)
137 			ErrorAlert(B_TRANSLATE("Cannot find default audio hardware"),
138 				fInitCheck);
139 		else
140 			ErrorAlert(B_TRANSLATE("Cannot connect to media server"),
141 				fInitCheck);
142 		PostMessage(B_QUIT_REQUESTED);
143 	} else
144 		Show();
145 }
146 
147 
~RecorderWindow()148 RecorderWindow::~RecorderWindow()
149 {
150 	//  The MediaRecorder have to be deleted, the dtor
151 	//  disconnect it from the media_kit.
152 	delete fRecorder;
153 
154 	delete fPlayer;
155 
156 	if (fPlayTrack && fPlayFile)
157 		fPlayFile->ReleaseTrack(fPlayTrack);
158 
159 	if (fPlayFile)
160 		delete fPlayFile;
161 	fPlayTrack = NULL;
162 	fPlayFile = NULL;
163 
164 	//	Clean up items in list view.
165 	if (fSoundList) {
166 		fSoundList->DeselectAll();
167 		for (int i = 0; i < fSoundList->CountItems(); i++) {
168 			WINDOW((stderr, "clean up item %d\n", i+1));
169 			SoundListItem* item = dynamic_cast<SoundListItem *>(fSoundList->ItemAt(i));
170 			if (item) {
171 				if (item->IsTemp())
172 					item->Entry().Remove();	//	delete temp file
173 				delete item;
174 			}
175 		}
176 		fSoundList->MakeEmpty();
177 	}
178 	//	Clean up currently recording file, if any.
179 	fRecEntry.Remove();
180 	fRecEntry.Unset();
181 
182 	delete fSavePanel;
183 }
184 
185 
186 status_t
InitCheck()187 RecorderWindow::InitCheck()
188 {
189 	return fInitCheck;
190 }
191 
192 
193 void
CalcSizes(float min_width,float min_height)194 RecorderWindow::CalcSizes(float min_width, float min_height)
195 {
196 	//	Set up size limits based on new screen size
197 	BScreen screen(this);
198 	BRect rect = screen.Frame();
199 	float width = rect.Width() - 12;
200 	SetSizeLimits(min_width, width, min_height, rect.Height() - 24);
201 
202 	//	Don't zoom to cover all of screen; user can resize last bit if necessary.
203 	//	This leaves other windows visible.
204 	if (width > 640)
205 		width = 640 + (width - 640) / 2;
206 	SetZoomLimits(width, rect.Height() - 24);
207 }
208 
209 
210 status_t
InitWindow()211 RecorderWindow::InitWindow()
212 {
213 	BPopUpMenu * popup = 0;
214 	status_t error;
215 
216 	try {
217 		//	Find temp directory for recorded sounds.
218 		BPath path;
219 		if (!(error = find_directory(B_SYSTEM_TEMP_DIRECTORY, &path)))
220 			error = fTempDir.SetTo(path.Path());
221 		if (error < 0)
222 			goto bad_mojo;
223 
224 		//	Make sure the media roster is there (which means the server is there).
225 		fRoster = BMediaRoster::Roster(&error);
226 		if (!fRoster)
227 			goto bad_mojo;
228 
229 		error = fRoster->GetAudioInput(&fAudioInputNode);
230 		if (error < B_OK) //	there's no input?
231 			goto bad_mojo;
232 
233 		error = fRoster->GetAudioMixer(&fAudioMixerNode);
234 		if (error < B_OK) //	there's no mixer?
235 			goto bad_mojo;
236 
237 		fRecorder = new BMediaRecorder("Sound Recorder",
238 			B_MEDIA_RAW_AUDIO);
239 
240 		if (fRecorder->InitCheck() < B_OK)
241 			goto bad_mojo;
242 
243 		// Set the node to accept only audio data
244 		media_format output_format;
245 		output_format.type = B_MEDIA_RAW_AUDIO;
246 		output_format.u.raw_audio = media_raw_audio_format::wildcard;
247 		fRecorder->SetAcceptedFormat(output_format);
248 
249 		//	Create the window header with controls
250 		BRect r(Bounds());
251 		r.bottom = r.top + 175;
252 		BBox *background = new BBox(r, "_background",
253 			B_FOLLOW_LEFT_RIGHT	| B_FOLLOW_TOP, B_WILL_DRAW
254 			| B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER);
255 
256 		AddChild(background);
257 
258 		r = background->Bounds();
259 		r.left = 0;
260 		r.right = r.left + 38;
261 		r.bottom = r.top + 104;
262 		fVUView = new VUView(r, B_FOLLOW_LEFT|B_FOLLOW_TOP);
263 		background->AddChild(fVUView);
264 
265 		r = background->Bounds();
266 		r.left = r.left + 40;
267 		r.bottom = r.top + 104;
268 		fScopeView = new ScopeView(r, B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP);
269 		background->AddChild(fScopeView);
270 
271 		r = background->Bounds();
272 		r.left = 2;
273 		r.right -= 26;
274 		r.top = 115;
275 		r.bottom = r.top + 30;
276 		fTrackSlider = new TrackSlider(r, "trackSlider", new BMessage(POSITION_CHANGED),
277 			B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP);
278 		background->AddChild(fTrackSlider);
279 
280 		BRect buttonRect;
281 
282 		//	Button for rewinding
283 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
284 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-7, 25));
285 		fRewindButton = new TransportButton(buttonRect, B_TRANSLATE("Rewind"),
286 			kSkipBackBitmapBits, kPressedSkipBackBitmapBits,
287 			kDisabledSkipBackBitmapBits, new BMessage(REWIND));
288 		background->AddChild(fRewindButton);
289 
290 		//	Button for stopping recording or playback
291 		buttonRect = BRect(BPoint(0,0), kStopButtonSize);
292 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-48, 25));
293 		fStopButton = new TransportButton(buttonRect, B_TRANSLATE("Stop"),
294 			kStopButtonBitmapBits, kPressedStopButtonBitmapBits,
295 			kDisabledStopButtonBitmapBits, new BMessage(STOP));
296 		background->AddChild(fStopButton);
297 
298 		//	Button for starting playback of selected sound
299 		BRect playRect(BPoint(0,0), kPlayButtonSize);
300 		playRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-82, 25));
301 		fPlayButton = new PlayPauseButton(playRect, B_TRANSLATE("Play"),
302 			new BMessage(PLAY), new BMessage(PLAY_PERIOD), ' ', 0);
303 		background->AddChild(fPlayButton);
304 
305 		//	Button for forwarding
306 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
307 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-133, 25));
308 		fForwardButton = new TransportButton(buttonRect, B_TRANSLATE("Forward"),
309 			kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits,
310 			kDisabledSkipForwardBitmapBits, new BMessage(FORWARD));
311 		background->AddChild(fForwardButton);
312 
313 		//	Button to start recording (or waiting for sound)
314 		buttonRect = BRect(BPoint(0,0), kRecordButtonSize);
315 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-174, 25));
316 		fRecordButton = new RecordButton(buttonRect, B_TRANSLATE("Record"),
317 			new BMessage(RECORD), new BMessage(RECORD_PERIOD));
318 		background->AddChild(fRecordButton);
319 
320 		//	Button for saving selected sound
321 		buttonRect = BRect(BPoint(0,0), kDiskButtonSize);
322 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-250, 21));
323 		fSaveButton = new TransportButton(buttonRect, B_TRANSLATE("Save"),
324 			kDiskButtonBitmapsBits, kPressedDiskButtonBitmapsBits,
325 			kDisabledDiskButtonBitmapsBits, new BMessage(SAVE));
326 		fSaveButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
327 		background->AddChild(fSaveButton);
328 
329 		//	Button Loop
330 		buttonRect = BRect(BPoint(0,0), kArrowSize);
331 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(23, 48));
332 		fLoopButton = new DrawButton(buttonRect, B_TRANSLATE("Loop"),
333 			kLoopArrowBits, kArrowBits, new BMessage(LOOP));
334 		fLoopButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
335 		fLoopButton->SetTarget(this);
336 		background->AddChild(fLoopButton);
337 
338 		buttonRect = BRect(BPoint(0,0), kSpeakerIconBitmapSize);
339 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(121, 17));
340 		SpeakerView *speakerView = new SpeakerView(buttonRect,
341 			B_FOLLOW_LEFT | B_FOLLOW_TOP);
342 		speakerView->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
343 		background->AddChild(speakerView);
344 
345 		buttonRect = BRect(BPoint(0,0), BPoint(84, 19));
346 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(107, 20));
347 		fVolumeSlider = new VolumeSlider(buttonRect, "volumeSlider",
348 			B_FOLLOW_LEFT | B_FOLLOW_TOP);
349 		fVolumeSlider->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
350 		background->AddChild(fVolumeSlider);
351 
352 		// Button to mask/see sounds list
353 		buttonRect = BRect(BPoint(0,0), kUpDownButtonSize);
354 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(21, 25));
355 		fUpDownButton = new UpDownButton(buttonRect, new BMessage(VIEW_LIST));
356 		fUpDownButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
357 		background->AddChild(fUpDownButton);
358 
359 		r = Bounds();
360 		r.top = background->Bounds().bottom + 1;
361 		fBottomBox = new BBox(r, "bottomBox", B_FOLLOW_ALL);
362 		fBottomBox->SetBorder(B_NO_BORDER);
363 		AddChild(fBottomBox);
364 
365 		//	The actual list of recorded sounds (initially empty) sits
366 		//	below the header with the controls.
367 		r = fBottomBox->Bounds();
368 		r.left += 190;
369 		r.InsetBy(10, 10);
370 		r.left -= 10;
371 		r.top += 4;
372 		r.right -= B_V_SCROLL_BAR_WIDTH;
373 		r.bottom -= 25;
374 		fSoundList = new SoundListView(r, B_TRANSLATE("Sound List"),
375 			B_FOLLOW_ALL);
376 		fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED));
377 		fSoundList->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
378 		BScrollView *scroller = new BScrollView("scroller", fSoundList,
379 			B_FOLLOW_ALL, 0, false, true, B_FANCY_BORDER);
380 		fBottomBox->AddChild(scroller);
381 
382 		r = fBottomBox->Bounds();
383 		r.right = r.left + 190;
384 		r.bottom -= 25;
385 		r.InsetBy(10, 8);
386 		r.top -= 1;
387 		fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT);
388 		fFileInfoBox->SetLabel(B_TRANSLATE("File info"));
389 
390 		fFileInfoBox->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
391 
392 		BFont font = be_plain_font;
393 		font.SetSize(font.Size() * 0.92f);
394 		font_height height;
395 		font.GetHeight(&height);
396 		float fontHeight = height.ascent + height.leading + height.descent;
397 
398 		r = fFileInfoBox->Bounds();
399 		r.left = 8;
400 		r.top = fontHeight + 6;
401 		r.bottom = r.top + fontHeight + 3;
402 		r.right -= 10;
403 		fFilename = new BStringView(r, "filename", B_TRANSLATE("File name:"));
404 		fFileInfoBox->AddChild(fFilename);
405 		fFilename->SetFont(&font, B_FONT_SIZE);
406 		fFilename->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
407 
408 		r.top += fontHeight;
409 		r.bottom = r.top + fontHeight + 3;
410 		fFormat = new BStringView(r, "format", B_TRANSLATE("Format:"));
411 		fFileInfoBox->AddChild(fFormat);
412 		fFormat->SetFont(&font, B_FONT_SIZE);
413 		fFormat->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
414 
415 		r.top += fontHeight;
416 		r.bottom = r.top + fontHeight + 3;
417 		fCompression = new BStringView(r, "compression",
418 			B_TRANSLATE("Compression:"));
419 		fFileInfoBox->AddChild(fCompression);
420 		fCompression->SetFont(&font, B_FONT_SIZE);
421 		fCompression->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
422 
423 		r.top += fontHeight;
424 		r.bottom = r.top + fontHeight + 3;
425 		fChannels = new BStringView(r, "channels", B_TRANSLATE("Channels:"));
426 		fFileInfoBox->AddChild(fChannels);
427 		fChannels->SetFont(&font, B_FONT_SIZE);
428 		fChannels->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
429 
430 		r.top += fontHeight;
431 		r.bottom = r.top + fontHeight + 3;
432 		fSampleSize = new BStringView(r, "samplesize",
433 			B_TRANSLATE("Sample size:"));
434 		fFileInfoBox->AddChild(fSampleSize);
435 		fSampleSize->SetFont(&font, B_FONT_SIZE);
436 		fSampleSize->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
437 
438 		r.top += fontHeight;
439 		r.bottom = r.top + fontHeight + 3;
440 		fSampleRate = new BStringView(r, "samplerate",
441 			B_TRANSLATE("Sample rate:"));
442 		fFileInfoBox->AddChild(fSampleRate);
443 		fSampleRate->SetFont(&font, B_FONT_SIZE);
444 		fSampleRate->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
445 
446 		r.top += fontHeight;
447 		r.bottom = r.top + fontHeight + 3;
448 		fDuration = new BStringView(r, "duration", B_TRANSLATE("Duration:"));
449 		fFileInfoBox->AddChild(fDuration);
450 		fDuration->SetFont(&font, B_FONT_SIZE);
451 		fDuration->SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
452 
453 		fFileInfoBox->ResizeTo(fFileInfoBox->Frame().Width(),
454 			r.bottom + fontHeight / 2.0f);
455 		fDeployedHeight = MIN_HEIGHT + fFileInfoBox->Bounds().Height() + 40.0f;
456 
457 		//	Input selection lists all available physical inputs that produce
458 		//	buffers with B_MEDIA_RAW_AUDIO format data.
459 		popup = new BPopUpMenu(B_TRANSLATE("Input"));
460 		const int maxInputCount = 64;
461 		dormant_node_info dni[maxInputCount];
462 
463 		int32 real_count = maxInputCount;
464 
465 		error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format,
466 			0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT);
467 		if (real_count > maxInputCount) {
468 			WINDOW((stderr, "dropped %" B_PRId32 " inputs\n", real_count - maxInputCount));
469 			real_count = maxInputCount;
470 		}
471 		char selected_name[B_MEDIA_NAME_LENGTH] = "Default input";
472 		BMessage * msg;
473 		BMenuItem * item;
474 		for (int i = 0; i < real_count; i++) {
475 			msg = new BMessage(INPUT_SELECTED);
476 			msg->AddData("node", B_RAW_TYPE, &dni[i], sizeof(dni[i]));
477 			item = new BMenuItem(dni[i].name, msg);
478 			popup->AddItem(item);
479 			media_node_id ni[12];
480 			int32 ni_count = 12;
481 			error = fRoster->GetInstancesFor(dni[i].addon, dni[i].flavor_id,
482 				ni, &ni_count);
483 			if (error == B_OK) {
484 				for (int j = 0; j < ni_count; j++) {
485 					if (ni[j] == fAudioInputNode.node) {
486 						strcpy(selected_name, dni[i].name);
487 						break;
488 					}
489 				}
490 			}
491 		}
492 
493 		//	Create the actual widget
494 		r = fFileInfoBox->Bounds();
495 		r.top = r.bottom + 2;
496 		r.bottom = r.top + 18;
497 		r.InsetBy(10, 10);
498 		fInputField = new BMenuField(r, "Input", B_TRANSLATE("Input:"), popup);
499 		fInputField->SetDivider(fInputField->StringWidth(B_TRANSLATE("Input:"))
500 			+ 4.0f);
501 		fBottomBox->AddChild(fInputField);
502 
503 		fBottomBox->AddChild(fFileInfoBox);
504 
505 		fBottomBox->Hide();
506 		CalcSizes(MIN_WIDTH, MIN_HEIGHT);
507 		ResizeTo(Frame().Width(), MIN_HEIGHT);
508 
509 		popup->Superitem()->SetLabel(selected_name);
510 
511 		// Make sure the save panel is happy.
512 		fSavePanel = new BFilePanel(B_SAVE_PANEL);
513 		fSavePanel->SetTarget(this);
514 	}
515 	catch (...) {
516 		goto bad_mojo;
517 	}
518 	UpdateButtons();
519 	return B_OK;
520 
521 	//	Error handling.
522 bad_mojo:
523 	if (error >= 0)
524 		error = B_ERROR;
525 	if (fRecorder)
526 		delete fRecorder;
527 
528 	delete fPlayer;
529 	if (!fInputField)
530 		delete popup;
531 	return error;
532 }
533 
534 
535 bool
QuitRequested()536 RecorderWindow::QuitRequested()	//	this means Close pressed
537 {
538 	StopRecording();
539 	StopPlaying();
540 	be_app->PostMessage(B_QUIT_REQUESTED);
541 	return true;
542 }
543 
544 
545 void
MessageReceived(BMessage * message)546 RecorderWindow::MessageReceived(BMessage * message)
547 {
548 	//	Your average generic message dispatching switch() statement.
549 	switch (message->what) {
550 	case INPUT_SELECTED:
551 		Input(message);
552 		break;
553 	case SOUND_SELECTED:
554 		Selected(message);
555 		break;
556 	case STOP_PLAYING:
557 		StopPlaying();
558 		break;
559 	case STOP_RECORDING:
560 		StopRecording();
561 		break;
562 	case PLAY_PERIOD:
563 		if (fPlayer) {
564 			if (fPlayer->HasData())
565 				fPlayButton->SetPlaying();
566 			else
567 				fPlayButton->SetPaused();
568 		}
569 		break;
570 	case RECORD_PERIOD:
571 		fRecordButton->SetRecording();
572 		break;
573 	case RECORD:
574 		Record(message);
575 		break;
576 	case STOP:
577 		Stop(message);
578 		break;
579 	case PLAY:
580 		Play(message);
581 		break;
582 	case SAVE:
583 		Save(message);
584 		break;
585 	case B_SAVE_REQUESTED:
586 		DoSave(message);
587 		break;
588 	case VIEW_LIST:
589 		if (fUpDownButton->Value() == B_CONTROL_ON) {
590 			fBottomBox->Show();
591 			CalcSizes(MIN_WIDTH, fDeployedHeight);
592 			ResizeTo(Frame().Width(), fDeployedHeight);
593 		} else {
594 			fBottomBox->Hide();
595 			CalcSizes(MIN_WIDTH, MIN_HEIGHT);
596 			ResizeTo(Frame().Width(), MIN_HEIGHT);
597 
598 		}
599 		break;
600 	case UPDATE_TRACKSLIDER:
601 		{
602 			bigtime_t timestamp = fPlayTrack->CurrentTime();
603 			fTrackSlider->SetMainTime(timestamp, false);
604 			fScopeView->SetMainTime(timestamp);
605 		}
606 		break;
607 	case POSITION_CHANGED:
608 		{
609 			bigtime_t right, left, main;
610 			if (message->FindInt64("main", &main) == B_OK) {
611 				if (fPlayTrack) {
612 					fPlayTrack->SeekToTime(fTrackSlider->MainTime());
613 					fPlayFrame = fPlayTrack->CurrentFrame();
614 				}
615 				fScopeView->SetMainTime(main);
616 			}
617 			if (message->FindInt64("right", &right) == B_OK) {
618 				if (fPlayTrack) {
619 					fPlayLimit = MIN(fPlayFrames,
620 						(off_t)(right * fPlayFormat.u.raw_audio.frame_rate
621 							/ 1000000LL));
622 				}
623 				fScopeView->SetRightTime(right);
624 			}
625 			if (message->FindInt64("left", &left) == B_OK)
626 				fScopeView->SetLeftTime(left);
627 			break;
628 		}
629 	case LOOP:
630 		fLooping = fLoopButton->ButtonState();
631 		break;
632 	case B_SIMPLE_DATA:
633 	case B_REFS_RECEIVED:
634 		{
635 			RefsReceived(message);
636 			break;
637 		}
638 	case B_COPY_TARGET:
639 		CopyTarget(message);
640 		break;
641 	default:
642 		BWindow::MessageReceived(message);
643 		break;
644 	}
645 }
646 
647 
648 void
Record(BMessage * message)649 RecorderWindow::Record(BMessage * message)
650 {
651 	//	User pressed Record button
652 	fRecording = true;
653 	if (fButtonState != btnPaused) {
654 		StopRecording();
655 		return;			//	user is too fast on the mouse
656 	}
657 	SetButtonState(btnRecording);
658 	fRecordButton->SetRecording();
659 
660 	char name[256];
661 	//	Create a file with a temporary name
662 	status_t err = NewTempName(name);
663 	if (err < B_OK) {
664 		ErrorAlert(B_TRANSLATE("Cannot find an unused name to use for the "
665 			"new recording"), err);
666 		return;
667 	}
668 	//	Find the file so we can refer to it later
669 	err = fTempDir.FindEntry(name, &fRecEntry);
670 	if (err < B_OK) {
671 		ErrorAlert(B_TRANSLATE("Cannot find the temporary file created to "
672 			"hold the new recording"), err);
673 		return;
674 	}
675 	err = fRecFile.SetTo(&fTempDir, name, O_RDWR);
676 	if (err < B_OK) {
677 		ErrorAlert(B_TRANSLATE("Cannot open the temporary file created to "
678 			"hold the new recording"), err);
679 		fRecEntry.Unset();
680 		return;
681 	}
682 	//	Reserve space on disk (creates fewer fragments)
683 	err = fRecFile.SetSize(4 * fRecordFormat.u.raw_audio.channel_count
684 		* fRecordFormat.u.raw_audio.frame_rate
685 		* (fRecordFormat.u.raw_audio.format
686 			& media_raw_audio_format::B_AUDIO_SIZE_MASK));
687 	if (err < B_OK) {
688 		ErrorAlert(B_TRANSLATE("Cannot record a sound that long"), err);
689 		fRecEntry.Remove();
690 		fRecEntry.Unset();
691 		return;
692 	}
693 	fRecSize = 0;
694 
695 	fRecFile.Seek(sizeof(struct wave_struct), SEEK_SET);
696 
697 	// Hook up input
698 	err = MakeRecordConnection(fAudioInputNode);
699 	if (err < B_OK) {
700 		ErrorAlert(B_TRANSLATE("Cannot connect to the selected sound input"),
701 			err);
702 		fRecEntry.Remove();
703 		fRecEntry.Unset();
704 		return;
705 	}
706 	fRecorder->Start();
707 }
708 
709 
710 void
Play(BMessage * message)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
Stop(BMessage * message)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
Save(BMessage * message)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
DoSave(BMessage * message)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
Input(BMessage * message)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
Selected(BMessage * message)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
MakeRecordConnection(const media_node & input)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 		// Get a format, any format.
896 		fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio;
897 		fExternalConnection = false;
898 	} else {
899 		fRecordFormat.u.raw_audio = fRecorder->AcceptedFormat().u.raw_audio;
900 		fExternalConnection = true;
901 	}
902 
903 	fRecordFormat.type = B_MEDIA_RAW_AUDIO;
904 
905 	//	Tell the consumer where we want data to go.
906 	err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this);
907 
908 	if (err < B_OK) {
909 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
910 			" couldn't set the sound recorder's hook functions\n"));
911 		return err;
912 	}
913 
914 	if (!fRecorder->IsConnected()) {
915 
916 		err = fRecorder->Connect(input, &audioOutput, &fRecordFormat);
917 
918 		if (err < B_OK) {
919 			CONNECT((stderr, "RecorderWindow::MakeRecordConnection():"
920 				" failed to connect sound recorder to audio input node.\n"));
921 
922 			fRecorder->SetHooks(NULL, NULL, NULL);
923 			return err;
924 		}
925 	}
926 
927 	return B_OK;
928 }
929 
930 
931 status_t
BreakRecordConnection()932 RecorderWindow::BreakRecordConnection()
933 {
934 	return fRecorder->Disconnect();
935 }
936 
937 
938 status_t
StopRecording()939 RecorderWindow::StopRecording()
940 {
941 	if (!fRecording)
942 		return B_OK;
943 	fRecording = false;
944 
945 	status_t err = B_OK;
946 	err = fRecorder->Stop(true);
947 	if (err < B_OK)
948 		return err;
949 
950 	// We maintain the connection active
951 	// if the user connected us from Cortex.
952 	if (!fExternalConnection) {
953 		BreakRecordConnection();
954 	}
955 
956 	fRecorder->SetHooks(NULL, NULL, NULL);
957 
958 	if (fRecSize > 0) {
959 
960 		wave_struct header;
961 		header.riff.riff_id = FOURCC('R','I','F','F');
962 		header.riff.len = fRecSize + sizeof(header) - 8;
963 		header.riff.wave_id = FOURCC('W','A','V','E');
964 		header.format_chunk.fourcc = FOURCC('f','m','t',' ');
965 		header.format_chunk.len = sizeof(header.format);
966 		header.format.format_tag = 1;
967 		header.format.channels = fRecordFormat.u.raw_audio.channel_count;
968 		header.format.samples_per_sec = (uint32)fRecordFormat.u.raw_audio.frame_rate;
969 		header.format.avg_bytes_per_sec = (uint32)(fRecordFormat.u.raw_audio.frame_rate
970 			* fRecordFormat.u.raw_audio.channel_count
971 			* (fRecordFormat.u.raw_audio.format & 0xf));
972 		header.format.bits_per_sample = (fRecordFormat.u.raw_audio.format & 0xf) * 8;
973 		header.format.block_align = (fRecordFormat.u.raw_audio.format & 0xf)
974 			* fRecordFormat.u.raw_audio.channel_count;
975 		header.data_chunk.fourcc = FOURCC('d','a','t','a');
976 		header.data_chunk.len = fRecSize;
977 		fRecFile.Seek(0, SEEK_SET);
978 		fRecFile.Write(&header, sizeof(header));
979 
980 		fRecFile.SetSize(fRecSize + sizeof(header));
981 		//	We reserve space; make sure we cut off any excess at the end.
982 		AddSoundItem(fRecEntry, true);
983 	} else
984 		fRecEntry.Remove();
985 
986 	//	We're done for this time.
987 	fRecEntry.Unset();
988 	//	Close the file.
989 	fRecFile.Unset();
990 	//	No more recording going on.
991 	fRecSize = 0;
992 	SetButtonState(btnPaused);
993 	fRecordButton->SetStopped();
994 
995 	return B_OK;
996 }
997 
998 
999 status_t
StopPlaying()1000 RecorderWindow::StopPlaying()
1001 {
1002 	if (fPlayer) {
1003 		fPlayer->Stop();
1004 		fPlayer->SetCallbacks(0, 0, 0);
1005 		fVolumeSlider->SetSoundPlayer(NULL);
1006 		delete fPlayer;
1007 		fPlayer = NULL;
1008 	}
1009 	SetButtonState(btnPaused);
1010 	fPlayButton->SetStopped();
1011 	fTrackSlider->ResetMainTime();
1012 	fScopeView->SetMainTime(*fTrackSlider->MainTime());
1013 	return B_OK;
1014 }
1015 
1016 
1017 void
SetButtonState(BtnState state)1018 RecorderWindow::SetButtonState(BtnState state)
1019 {
1020 	fButtonState = state;
1021 	UpdateButtons();
1022 }
1023 
1024 
1025 void
UpdateButtons()1026 RecorderWindow::UpdateButtons()
1027 {
1028 	bool hasSelection = (fSoundList->CurrentSelection() >= 0);
1029 	fRecordButton->SetEnabled(fButtonState != btnPlaying);
1030 	fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
1031 	fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
1032 	fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
1033 	fStopButton->SetEnabled(fButtonState != btnPaused);
1034 	fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording));
1035 	fInputField->SetEnabled(fButtonState != btnRecording);
1036 }
1037 
1038 #ifndef __HAIKU__
1039 extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(
1040 	BMediaTrack *self, media_format *inout_format);
1041 #endif
1042 
1043 
1044 status_t
UpdatePlayFile(SoundListItem * item,bool updateDisplay)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 = B_TRANSLATE("File name: ");
1097 	filename << ref.name;
1098 	fFilename->SetText(filename.String());
1099 
1100 	BString format = B_TRANSLATE("Format: ");
1101 	media_file_format file_format;
1102 	if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK)
1103 		format << file_format.short_name;
1104 	BString compression = B_TRANSLATE("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 << B_TRANSLATE("None");
1109 		else
1110 			compression << codec_info.short_name;
1111 	}
1112 	BString channels = B_TRANSLATE("Channels: ");
1113 	channels << fPlayFormat.u.raw_audio.channel_count;
1114 	BString samplesize = B_TRANSLATE("Sample size: ");
1115 	samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf)
1116 		<< B_TRANSLATE(" bits");
1117 	BString samplerate = B_TRANSLATE("Sample rate: ");
1118 	samplerate << (int)fPlayFormat.u.raw_audio.frame_rate;
1119 	BString durationString = B_TRANSLATE("Duration: ");
1120 	bigtime_t duration = fPlayTrack->Duration();
1121 	durationString << (float)(duration / 1000000.0) << B_TRANSLATE(" seconds");
1122 
1123 	fFormat->SetText(format.String());
1124 	fCompression->SetText(compression.String());
1125 	fChannels->SetText(channels.String());
1126 	fSampleSize->SetText(samplesize.String());
1127 	fSampleRate->SetText(samplerate.String());
1128 	fDuration->SetText(durationString.String());
1129 
1130 	fTrackSlider->SetTotalTime(duration, true);
1131 	fScopeView->SetTotalTime(duration, true);
1132 	fScopeView->RenderTrack(fPlayTrack, fPlayFormat);
1133 
1134 	fPlayFrames = fPlayTrack->CountFrames();
1135 	return B_OK;
1136 }
1137 
1138 
1139 void
ErrorAlert(const char * action,status_t err)1140 RecorderWindow::ErrorAlert(const char * action, status_t err)
1141 {
1142 	char msg[300];
1143 	if (err != B_OK)
1144 		sprintf(msg, "%s: %s. [%" B_PRIx32 "]", action, strerror(err), (int32) err);
1145 	else
1146 		sprintf(msg, "%s.", action);
1147 	BAlert* alert = new BAlert("", msg, B_TRANSLATE("Stop"));
1148 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1149 	alert->Go();
1150 }
1151 
1152 
1153 status_t
NewTempName(char * name)1154 RecorderWindow::NewTempName(char * name)
1155 {
1156 	int init_count = fTempCount;
1157 again:
1158 	if (fTempCount-init_count > 25) {
1159 		return B_ERROR;
1160 	}
1161 	else {
1162 		fTempCount++;
1163 		if (fTempCount==0)
1164 			sprintf(name, "Audio Clip");
1165 		else
1166 			sprintf(name, "Audio Clip %d", fTempCount);
1167 		BPath path;
1168 		status_t err;
1169 		BEntry tempEnt;
1170 		if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) {
1171 			return err;
1172 		}
1173 		if ((err = tempEnt.GetPath(&path)) < B_OK) {
1174 			return err;
1175 		}
1176 		path.Append(name);
1177 		int fd;
1178 		//	Use O_EXCL so we know we created the file (sync with other instances)
1179 		if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
1180 			goto again;
1181 		}
1182 		close(fd);
1183 	}
1184 	return B_OK;
1185 }
1186 
1187 
1188 void
AddSoundItem(const BEntry & entry,bool temp)1189 RecorderWindow::AddSoundItem(const BEntry& entry, bool temp)
1190 {
1191 	//	Create list item to display.
1192 	SoundListItem * listItem = new SoundListItem(entry, temp);
1193 	fSoundList->AddItem(listItem);
1194 	fSoundList->Invalidate();
1195 	fSoundList->Select(fSoundList->IndexOf(listItem));
1196 }
1197 
1198 
1199 void
RemoveCurrentSoundItem()1200 RecorderWindow::RemoveCurrentSoundItem() {
1201 	int32 index = fSoundList->CurrentSelection();
1202 	BListItem *item = fSoundList->RemoveItem(index);
1203 	delete item;
1204 	if (index >= fSoundList->CountItems())
1205 		index = fSoundList->CountItems() - 1;
1206 	fSoundList->Select(index);
1207 }
1208 
1209 
1210 void
RecordFile(void * cookie,bigtime_t timestamp,void * data,size_t size,const media_format & format)1211 RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp,
1212 	void* data, size_t size, const media_format &format)
1213 {
1214 	//	Callback called from the SoundConsumer when receiving buffers.
1215 	RecorderWindow * window = (RecorderWindow *)cookie;
1216 
1217 	if (window->fRecording) {
1218 		//	Write the data to file (we don't buffer or guard file access
1219 		//	or anything)
1220 		window->fRecFile.WriteAt(window->fRecSize, data, size);
1221 		window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format);
1222 		window->fRecSize += size;
1223 	}
1224 }
1225 
1226 
1227 void
NotifyRecordFile(void * cookie,BMediaRecorder::notification code,...)1228 RecorderWindow::NotifyRecordFile(void * cookie,
1229 	BMediaRecorder::notification code, ...)
1230 {
1231 	if (code == BMediaRecorder::B_WILL_STOP) {
1232 		RecorderWindow * window = (RecorderWindow *)cookie;
1233 		// Tell the window we've stopped, if it doesn't
1234 		// already know.
1235 		window->PostMessage(STOP_RECORDING);
1236 	}
1237 }
1238 
1239 
1240 void
PlayFile(void * cookie,void * data,size_t size,const media_raw_audio_format & format)1241 RecorderWindow::PlayFile(void * cookie, void * data, size_t size,
1242 	const media_raw_audio_format & format)
1243 {
1244 	//	Callback called from the SoundProducer when producing buffers.
1245 	RecorderWindow * window = (RecorderWindow *)cookie;
1246 	int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) *
1247 		window->fPlayFormat.u.raw_audio.channel_count;
1248 
1249 	if ((window->fPlayFrame < window->fPlayLimit) || window->fLooping) {
1250 		if (window->fPlayFrame >= window->fPlayLimit) {
1251 			bigtime_t left = window->fTrackSlider->LeftTime();
1252 			window->fPlayTrack->SeekToTime(&left);
1253 			window->fPlayFrame = window->fPlayTrack->CurrentFrame();
1254 		}
1255 		int64 frames = 0;
1256 		window->fPlayTrack->ReadFrames(data, &frames);
1257 		window->fVUView->ComputeLevels(data, size / frame_size, format.format);
1258 		window->fPlayFrame += size/frame_size;
1259 		window->PostMessage(UPDATE_TRACKSLIDER);
1260 	} else {
1261 		//	we're done!
1262 		window->PostMessage(STOP_PLAYING);
1263 	}
1264 }
1265 
1266 
1267 void
NotifyPlayFile(void * cookie,BSoundPlayer::sound_player_notification code,...)1268 RecorderWindow::NotifyPlayFile(void * cookie,
1269 	BSoundPlayer::sound_player_notification code, ...)
1270 {
1271 	if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) {
1272 		RecorderWindow * window = (RecorderWindow *)cookie;
1273 		// tell the window we've stopped, if it doesn't
1274 		// already know.
1275 		window->PostMessage(STOP_PLAYING);
1276 	}
1277 }
1278 
1279 
1280 void
RefsReceived(BMessage * msg)1281 RecorderWindow::RefsReceived(BMessage *msg)
1282 {
1283 	entry_ref ref;
1284 	int32 i = 0;
1285 	int32 countGood = 0;
1286 	int32 countBad = 0;
1287 
1288 	while (msg->FindRef("refs", i++, &ref) == B_OK) {
1289 
1290 		BEntry entry(&ref, true);
1291 		BPath path(&entry);
1292 		BNode node(&entry);
1293 
1294 		if (node.IsFile()) {
1295 			SoundListItem * listItem = new SoundListItem(entry, false);
1296 			if (UpdatePlayFile(listItem) == B_OK) {
1297 				fSoundList->AddItem(listItem);
1298 				countGood++;
1299 				continue;
1300 			}
1301 			delete listItem;
1302 		} else if(node.IsDirectory()) {
1303 
1304 		}
1305 		countBad++;
1306 	}
1307 
1308 	if (countBad == 1 && countGood == 0) {
1309 		BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1310 			B_TRANSLATE("The file doesn't appear to be an audio file."),
1311 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1312 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1313 		alert->Go();
1314 	} else if (countBad > 0 && countGood == 0) {
1315 		BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"),
1316 			B_TRANSLATE("None of the files appear to be audio files."),
1317 			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1318 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1319 		alert->Go();
1320 	} else if (countGood > 0) {
1321 		if (countBad > 0) {
1322 			BAlert* alert = new BAlert(B_TRANSLATE("Invalid audio files"),
1323 			B_TRANSLATE("Some of the files don't appear to be audio files."),
1324 				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
1325 				B_WARNING_ALERT);
1326 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1327 			alert->Go();
1328 		}
1329 		fSoundList->Select(fSoundList->CountItems() - 1);
1330 	}
1331 }
1332 
1333 
1334 void
CopyTarget(BMessage * msg)1335 RecorderWindow::CopyTarget(BMessage *msg)
1336 {
1337 	const char *type = NULL;
1338 	if (msg->FindString("be:types", &type) == B_OK) {
1339 		if (!strcasecmp(type, B_FILE_MIME_TYPE)) {
1340 			const char *name;
1341 			entry_ref dir;
1342 			if (msg->FindString("be:filetypes") != NULL
1343 				&& msg->FindString("name", &name) == B_OK
1344 				&& msg->FindRef("directory", &dir) == B_OK) {
1345 				BDirectory directory(&dir);
1346 				BFile file(&directory, name, O_RDWR | O_TRUNC);
1347 
1348 				// seek time
1349 				bigtime_t start = fTrackSlider->LeftTime();
1350 
1351 				// write data
1352 				bigtime_t diffTime = fTrackSlider->RightTime()
1353 					- fTrackSlider->LeftTime();
1354 				int64 framesToWrite = (int64) (diffTime
1355 					* fPlayFormat.u.raw_audio.frame_rate / 1000000LL);
1356 				int32 frameSize = (fPlayFormat.u.raw_audio.format & 0xf)
1357 					* fPlayFormat.u.raw_audio.channel_count;
1358 
1359 				wave_struct header;
1360 				header.riff.riff_id = FOURCC('R','I','F','F');
1361 				header.riff.len
1362 					= (frameSize * framesToWrite) + sizeof(header) - 8;
1363 				header.riff.wave_id = FOURCC('W','A','V','E');
1364 				header.format_chunk.fourcc = FOURCC('f','m','t',' ');
1365 				header.format_chunk.len = sizeof(header.format);
1366 				header.format.format_tag = 1;
1367 				header.format.channels = fPlayFormat.u.raw_audio.channel_count;
1368 				header.format.samples_per_sec
1369 					= (uint32)fPlayFormat.u.raw_audio.frame_rate;
1370 				header.format.avg_bytes_per_sec
1371 					= (uint32)(fPlayFormat.u.raw_audio.frame_rate
1372 					* fPlayFormat.u.raw_audio.channel_count
1373 					* (fPlayFormat.u.raw_audio.format & 0xf));
1374 				header.format.bits_per_sample
1375 					= (fPlayFormat.u.raw_audio.format & 0xf) * 8;
1376 				header.format.block_align = frameSize;
1377 				header.data_chunk.fourcc = FOURCC('d','a','t','a');
1378 				header.data_chunk.len = frameSize * framesToWrite;
1379 				file.Seek(0, SEEK_SET);
1380 				file.Write(&header, sizeof(header));
1381 
1382 				char *data = (char *)malloc(fPlayFormat.u.raw_audio.buffer_size);
1383 
1384 				fPlayTrack->SeekToTime(&start);
1385 				fPlayFrame = fPlayTrack->CurrentFrame();
1386 				while (framesToWrite > 0) {
1387 					int64 frames = 0;
1388 					status_t err = fPlayTrack->ReadFrames(data, &frames);
1389 					if (frames <= 0 || err != B_OK) {
1390 						if (err != B_OK)
1391 							fprintf(stderr, "CopyTarget: ReadFrames failed\n");
1392 						break;
1393 					}
1394 					file.Write(data, frames * frameSize);
1395 					framesToWrite -= frames;
1396 				}
1397 
1398 				file.Sync();
1399 				free(data);
1400 				BNodeInfo nodeInfo(&file);
1401 				// set type
1402 			}
1403 		} else {
1404 
1405 		}
1406 	}
1407 }
1408