xref: /haiku/src/apps/soundrecorder/RecorderWindow.cpp (revision a4797804ba5d64a64a3f876ad54c85263464e41b)
157e2f323SJérôme Duval /*
257e2f323SJérôme Duval  * Copyright 2005, Jérôme Duval. All rights reserved.
357e2f323SJérôme Duval  * Distributed under the terms of the MIT License.
457e2f323SJérôme Duval  *
557e2f323SJérôme Duval  * Inspired by SoundCapture from Be newsletter (Media Kit Basics: Consumers and Producers)
657e2f323SJérôme Duval  */
757e2f323SJérôme Duval 
857e2f323SJérôme Duval #include <Application.h>
957e2f323SJérôme Duval #include <Alert.h>
1057e2f323SJérôme Duval #include <Debug.h>
1157e2f323SJérôme Duval #include <Screen.h>
1257e2f323SJérôme Duval #include <Button.h>
1357e2f323SJérôme Duval #include <CheckBox.h>
1457e2f323SJérôme Duval #include <TextControl.h>
1557e2f323SJérôme Duval #include <MenuField.h>
1657e2f323SJérôme Duval #include <PopUpMenu.h>
1757e2f323SJérôme Duval #include <MenuItem.h>
1857e2f323SJérôme Duval #include <Box.h>
1957e2f323SJérôme Duval #include <ScrollView.h>
2057e2f323SJérôme Duval #include <Beep.h>
2157e2f323SJérôme Duval #include <StringView.h>
2257e2f323SJérôme Duval #include <String.h>
2357e2f323SJérôme Duval #include <Slider.h>
2457e2f323SJérôme Duval #include <Message.h>
2557e2f323SJérôme Duval 
2657e2f323SJérôme Duval #include <Path.h>
2757e2f323SJérôme Duval #include <FindDirectory.h>
2857e2f323SJérôme Duval #include <MediaAddOn.h>
2957e2f323SJérôme Duval 
3057e2f323SJérôme Duval #include <SoundPlayer.h>
3157e2f323SJérôme Duval 
3257e2f323SJérôme Duval #include <assert.h>
3357e2f323SJérôme Duval #include <stdio.h>
3457e2f323SJérôme Duval #include <string.h>
3557e2f323SJérôme Duval #include <stdlib.h>
3657e2f323SJérôme Duval #include <ctype.h>
3757e2f323SJérôme Duval #include <unistd.h>
38338b8dc3SIngo Weinhold #include <fcntl.h>
3957e2f323SJérôme Duval 
4057e2f323SJérôme Duval #include <MediaRoster.h>
4157e2f323SJérôme Duval #include <TimeSource.h>
42019ed09bSJérôme Duval #include <NodeInfo.h>
4357e2f323SJérôme Duval 
4457e2f323SJérôme Duval #include "RecorderWindow.h"
4557e2f323SJérôme Duval #include "SoundConsumer.h"
4657e2f323SJérôme Duval #include "FileUtils.h"
4757e2f323SJérôme Duval 
4857e2f323SJérôme Duval #if ! NDEBUG
4957e2f323SJérôme Duval #define FPRINTF(args) fprintf args
5057e2f323SJérôme Duval #else
5157e2f323SJérôme Duval #define FPRINTF(args)
5257e2f323SJérôme Duval #endif
5357e2f323SJérôme Duval 
5457e2f323SJérôme Duval #define DEATH FPRINTF
5557e2f323SJérôme Duval #define CONNECT FPRINTF
5657e2f323SJérôme Duval #define WINDOW FPRINTF
5757e2f323SJérôme Duval 
5857e2f323SJérôme Duval // default window positioning
5957e2f323SJérôme Duval static const float MIN_WIDTH = 400.0f;
6057e2f323SJérôme Duval static const float MIN_HEIGHT = 336.0f;
6157e2f323SJérôme Duval static const float XPOS = 100.0f;
6257e2f323SJérôme Duval static const float YPOS = 200.0f;
6357e2f323SJérôme Duval 
6457e2f323SJérôme Duval #define FOURCC(a,b,c,d)	((((uint32)(d)) << 24) | (((uint32)(c)) << 16) | (((uint32)(b)) << 8) | ((uint32)(a)))
6557e2f323SJérôme Duval 
6657e2f323SJérôme Duval struct riff_struct
6757e2f323SJérôme Duval {
6857e2f323SJérôme Duval 	uint32 riff_id; // 'RIFF'
6957e2f323SJérôme Duval 	uint32 len;
7057e2f323SJérôme Duval 	uint32 wave_id;	// 'WAVE'
7157e2f323SJérôme Duval };
7257e2f323SJérôme Duval 
7357e2f323SJérôme Duval struct chunk_struct
7457e2f323SJérôme Duval {
7557e2f323SJérôme Duval 	uint32 fourcc;
7657e2f323SJérôme Duval 	uint32 len;
7757e2f323SJérôme Duval };
7857e2f323SJérôme Duval 
7957e2f323SJérôme Duval struct format_struct
8057e2f323SJérôme Duval {
8157e2f323SJérôme Duval 	uint16 format_tag;
8257e2f323SJérôme Duval 	uint16 channels;
8357e2f323SJérôme Duval 	uint32 samples_per_sec;
8457e2f323SJérôme Duval 	uint32 avg_bytes_per_sec;
8557e2f323SJérôme Duval 	uint16 block_align;
8657e2f323SJérôme Duval 	uint16 bits_per_sample;
8757e2f323SJérôme Duval };
8857e2f323SJérôme Duval 
8957e2f323SJérôme Duval 
9057e2f323SJérôme Duval struct wave_struct
9157e2f323SJérôme Duval {
9257e2f323SJérôme Duval 	struct riff_struct riff;
9357e2f323SJérôme Duval 	struct chunk_struct format_chunk;
9457e2f323SJérôme Duval 	struct format_struct format;
9557e2f323SJérôme Duval 	struct chunk_struct data_chunk;
9657e2f323SJérôme Duval };
9757e2f323SJérôme Duval 
9857e2f323SJérôme Duval 
9957e2f323SJérôme Duval RecorderWindow::RecorderWindow() :
10057e2f323SJérôme Duval 	BWindow(BRect(XPOS,YPOS,XPOS+MIN_WIDTH,YPOS+MIN_HEIGHT), "SoundRecorder", B_TITLED_WINDOW,
10143450d12SJérôme Duval 		B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE),
10257e2f323SJérôme Duval 		fPlayer(NULL),
1035fa77532SJérôme Duval 		fSoundList(NULL),
10457e2f323SJérôme Duval 		fPlayFile(NULL),
10557e2f323SJérôme Duval 		fPlayTrack(NULL),
1067942339dSJérôme Duval 		fPlayFrames(0),
107948356deSJérôme Duval 		fLooping(false),
1085fa77532SJérôme Duval 		fSavePanel(NULL),
1095fa77532SJérôme Duval 		fInitCheck(B_OK)
11057e2f323SJérôme Duval {
11157e2f323SJérôme Duval 	fRoster = NULL;
11257e2f323SJérôme Duval 	fRecordButton = NULL;
11357e2f323SJérôme Duval 	fPlayButton = NULL;
11457e2f323SJérôme Duval 	fStopButton = NULL;
11557e2f323SJérôme Duval 	fSaveButton = NULL;
116948356deSJérôme Duval 	fLoopButton = NULL;
11757e2f323SJérôme Duval 	fInputField = NULL;
11857e2f323SJérôme Duval 	fRecordNode = 0;
11957e2f323SJérôme Duval 	fRecording = false;
12057e2f323SJérôme Duval 	fTempCount = -1;
12157e2f323SJérôme Duval 	fButtonState = btnPaused;
12257e2f323SJérôme Duval 
12357e2f323SJérôme Duval 	CalcSizes(MIN_WIDTH, MIN_HEIGHT);
12457e2f323SJérôme Duval 
1255fa77532SJérôme Duval 	fInitCheck = InitWindow();
1265fa77532SJérôme Duval 	if (fInitCheck != B_OK) {
127bdb1d3acSJérôme Duval 		if (fInitCheck == B_NAME_NOT_FOUND)
128bdb1d3acSJérôme Duval 			ErrorAlert("find default audio hardware", fInitCheck);
129bdb1d3acSJérôme Duval 		else
1305fa77532SJérôme Duval 			ErrorAlert("connect to media server", fInitCheck);
1315fa77532SJérôme Duval 		PostMessage(B_QUIT_REQUESTED);
1325fa77532SJérôme Duval 	} else
1335fa77532SJérôme Duval 		Show();
13457e2f323SJérôme Duval }
13557e2f323SJérôme Duval 
13657e2f323SJérôme Duval RecorderWindow::~RecorderWindow()
13757e2f323SJérôme Duval {
13857e2f323SJérôme Duval 	//	The sound consumer and producer are Nodes; it has to be Release()d and the Roster
13957e2f323SJérôme Duval 	//	will reap it when it's done.
14057e2f323SJérôme Duval 	if (fRecordNode) {
14157e2f323SJérôme Duval 		fRecordNode->Release();
14257e2f323SJérôme Duval 	}
14357e2f323SJérôme Duval 	if (fPlayer) {
14457e2f323SJérôme Duval 		delete fPlayer;
14557e2f323SJérôme Duval 	}
1467942339dSJérôme Duval 
1477942339dSJérôme Duval 	if (fPlayTrack && fPlayFile)
1487942339dSJérôme Duval 		fPlayFile->ReleaseTrack(fPlayTrack);
1497942339dSJérôme Duval 	if (fPlayFile)
1507942339dSJérôme Duval 		delete fPlayFile;
1517942339dSJérôme Duval 	fPlayTrack = NULL;
1527942339dSJérôme Duval 	fPlayFile = NULL;
1537942339dSJérôme Duval 
15457e2f323SJérôme Duval 	//	Clean up items in list view.
15557e2f323SJérôme Duval 	if (fSoundList) {
15657e2f323SJérôme Duval 		fSoundList->DeselectAll();
15757e2f323SJérôme Duval 		for (int ix=0; ix<fSoundList->CountItems(); ix++) {
15857e2f323SJérôme Duval 			WINDOW((stderr, "clean up item %d\n", ix+1));
15957e2f323SJérôme Duval 			SoundListItem * item = dynamic_cast<SoundListItem *>(fSoundList->ItemAt(ix));
16057e2f323SJérôme Duval 			if (item) {
16157e2f323SJérôme Duval 				if (item->IsTemp()) {
16257e2f323SJérôme Duval 					item->Entry().Remove();	//	delete temp file
16357e2f323SJérôme Duval 				}
16457e2f323SJérôme Duval 				delete item;
16557e2f323SJérôme Duval 			}
16657e2f323SJérôme Duval 		}
16757e2f323SJérôme Duval 		fSoundList->MakeEmpty();
16857e2f323SJérôme Duval 	}
16957e2f323SJérôme Duval 	//	Clean up currently recording file, if any.
17057e2f323SJérôme Duval 	fRecEntry.Remove();
17157e2f323SJérôme Duval 	fRecEntry.Unset();
17205ea8535SKarsten Heimrich 
17305ea8535SKarsten Heimrich 	delete fSavePanel;
17457e2f323SJérôme Duval }
17557e2f323SJérôme Duval 
17657e2f323SJérôme Duval 
1775fa77532SJérôme Duval status_t
1785fa77532SJérôme Duval RecorderWindow::InitCheck()
1795fa77532SJérôme Duval {
1805fa77532SJérôme Duval 	return fInitCheck;
1815fa77532SJérôme Duval }
1825fa77532SJérôme Duval 
1835fa77532SJérôme Duval 
18457e2f323SJérôme Duval void
18557e2f323SJérôme Duval RecorderWindow::CalcSizes(float min_wid, float min_hei)
18657e2f323SJérôme Duval {
18757e2f323SJérôme Duval 	//	Set up size limits based on new screen size
1885e99b7dfSJérôme Duval 	BScreen screen(this);
18957e2f323SJérôme Duval 	BRect r = screen.Frame();
19057e2f323SJérôme Duval 	float wid = r.Width()-12;
19157e2f323SJérôme Duval 	SetSizeLimits(min_wid, wid, min_hei, r.Height()-24);
19257e2f323SJérôme Duval 
19357e2f323SJérôme Duval 	//	Don't zoom to cover all of screen; user can resize last bit if necessary.
19457e2f323SJérôme Duval 	//	This leaves other windows visible.
19557e2f323SJérôme Duval 	if (wid > 640) {
19657e2f323SJérôme Duval 		wid = 640 + (wid-640)/2;
19757e2f323SJérôme Duval 	}
19857e2f323SJérôme Duval 	SetZoomLimits(wid, r.Height()-24);
19957e2f323SJérôme Duval }
20057e2f323SJérôme Duval 
20157e2f323SJérôme Duval 
20257e2f323SJérôme Duval status_t
20357e2f323SJérôme Duval RecorderWindow::InitWindow()
20457e2f323SJérôme Duval {
20557e2f323SJérôme Duval 	BPopUpMenu * popup = 0;
20657e2f323SJérôme Duval 	status_t error;
20757e2f323SJérôme Duval 
20857e2f323SJérôme Duval 	try {
20957e2f323SJérôme Duval 		//	Find temp directory for recorded sounds.
21057e2f323SJérôme Duval 		BPath path;
21157e2f323SJérôme Duval 		if (!(error = find_directory(B_COMMON_TEMP_DIRECTORY, &path))) {
21257e2f323SJérôme Duval 			error = fTempDir.SetTo(path.Path());
21357e2f323SJérôme Duval 		}
21457e2f323SJérôme Duval 		if (error < 0) {
21557e2f323SJérôme Duval 			goto bad_mojo;
21657e2f323SJérôme Duval 		}
21757e2f323SJérôme Duval 
21857e2f323SJérôme Duval 		//	Make sure the media roster is there (which means the server is there).
21957e2f323SJérôme Duval 		fRoster = BMediaRoster::Roster(&error);
22057e2f323SJérôme Duval 		if (!fRoster) {
22157e2f323SJérôme Duval 			goto bad_mojo;
22257e2f323SJérôme Duval 		}
22357e2f323SJérôme Duval 
22457e2f323SJérôme Duval 		error = fRoster->GetAudioInput(&fAudioInputNode);
22557e2f323SJérôme Duval 		if (error < B_OK) {	//	there's no input?
22657e2f323SJérôme Duval 			goto bad_mojo;
22757e2f323SJérôme Duval 		}
22857e2f323SJérôme Duval 
22957e2f323SJérôme Duval 		error = fRoster->GetAudioMixer(&fAudioMixerNode);
23057e2f323SJérôme Duval 		if (error < B_OK) {	//	there's no mixer?
23157e2f323SJérôme Duval 			goto bad_mojo;
23257e2f323SJérôme Duval 		}
23357e2f323SJérôme Duval 
23457e2f323SJérôme Duval 		//	Create our internal Node which records sound, and register it.
23557e2f323SJérôme Duval 		fRecordNode = new SoundConsumer("Sound Recorder");
23657e2f323SJérôme Duval 		error = fRoster->RegisterNode(fRecordNode);
23757e2f323SJérôme Duval 		if (error < B_OK) {
23857e2f323SJérôme Duval 			goto bad_mojo;
23957e2f323SJérôme Duval 		}
24057e2f323SJérôme Duval 
24157e2f323SJérôme Duval 		//	Create the window header with controls
24257e2f323SJérôme Duval 		BRect r(Bounds());
24357e2f323SJérôme Duval 		r.bottom = r.top + 175;
24457e2f323SJérôme Duval 		BBox *background = new BBox(r, "_background", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP,
245*a4797804SJérôme Duval 			B_WILL_DRAW|B_FRAME_EVENTS|B_NAVIGABLE_JUMP, B_NO_BORDER);
24657e2f323SJérôme Duval 		AddChild(background);
24757e2f323SJérôme Duval 
24857e2f323SJérôme Duval 
24957e2f323SJérôme Duval 
25057e2f323SJérôme Duval 		r = background->Bounds();
251*a4797804SJérôme Duval 		r.left = 0;
252*a4797804SJérôme Duval 		r.right = r.left + 35;
25357e2f323SJérôme Duval 		r.bottom = r.top + 104;
25457e2f323SJérôme Duval 		fVUView = new VUView(r, B_FOLLOW_LEFT|B_FOLLOW_TOP);
25557e2f323SJérôme Duval 		background->AddChild(fVUView);
25657e2f323SJérôme Duval 
25757e2f323SJérôme Duval 		r = background->Bounds();
258*a4797804SJérôme Duval 		r.left = r.left + 38;
25957e2f323SJérôme Duval 		r.bottom = r.top + 104;
26057e2f323SJérôme Duval 		fScopeView = new ScopeView(r, B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP);
26157e2f323SJérôme Duval 		background->AddChild(fScopeView);
26257e2f323SJérôme Duval 
26357e2f323SJérôme Duval 		r = background->Bounds();
26457e2f323SJérôme Duval 		r.left = 2;
26557e2f323SJérôme Duval 		r.right -= 26;
26657e2f323SJérôme Duval 		r.top = 115;
26757e2f323SJérôme Duval 		r.bottom = r.top + 30;
26857e2f323SJérôme Duval 		fTrackSlider = new TrackSlider(r, "trackSlider", new BMessage(POSITION_CHANGED), B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP);
26957e2f323SJérôme Duval 		background->AddChild(fTrackSlider);
27057e2f323SJérôme Duval 
27157e2f323SJérôme Duval 		BRect buttonRect;
27257e2f323SJérôme Duval 
27357e2f323SJérôme Duval 		//	Button for rewinding
27457e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
27557e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-7, 25));
27657e2f323SJérôme Duval 		fRewindButton = new TransportButton(buttonRect, "Rewind",
27757e2f323SJérôme Duval 			kSkipBackBitmapBits, kPressedSkipBackBitmapBits, kDisabledSkipBackBitmapBits,
27857e2f323SJérôme Duval 			new BMessage(REWIND));
27957e2f323SJérôme Duval 		background->AddChild(fRewindButton);
28057e2f323SJérôme Duval 
28157e2f323SJérôme Duval 		//	Button for stopping recording or playback
28257e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kStopButtonSize);
28357e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-48, 25));
28457e2f323SJérôme Duval 		fStopButton = new TransportButton(buttonRect, "Stop",
28557e2f323SJérôme Duval 			kStopButtonBitmapBits, kPressedStopButtonBitmapBits, kDisabledStopButtonBitmapBits,
28657e2f323SJérôme Duval 			new BMessage(STOP));
28757e2f323SJérôme Duval 		background->AddChild(fStopButton);
28857e2f323SJérôme Duval 
28957e2f323SJérôme Duval 		//	Button for starting playback of selected sound
29057e2f323SJérôme Duval 		BRect playRect(BPoint(0,0), kPlayButtonSize);
29157e2f323SJérôme Duval 		playRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-82, 25));
29257e2f323SJérôme Duval 		fPlayButton = new PlayPauseButton(playRect, "Play",
29357e2f323SJérôme Duval 			new BMessage(PLAY), new BMessage(PLAY_PERIOD), ' ', 0);
29457e2f323SJérôme Duval 		background->AddChild(fPlayButton);
29557e2f323SJérôme Duval 
29657e2f323SJérôme Duval 		//	Button for forwarding
29757e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kSkipButtonSize);
29857e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-133, 25));
29957e2f323SJérôme Duval 		fForwardButton = new TransportButton(buttonRect, "Forward",
30057e2f323SJérôme Duval 			kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits, kDisabledSkipForwardBitmapBits,
30157e2f323SJérôme Duval 			new BMessage(FORWARD));
30257e2f323SJérôme Duval 		background->AddChild(fForwardButton);
30357e2f323SJérôme Duval 
30457e2f323SJérôme Duval 		//	Button to start recording (or waiting for sound)
30557e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kRecordButtonSize);
30657e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-174, 25));
30757e2f323SJérôme Duval 		fRecordButton = new RecordButton(buttonRect, "Record",
30857e2f323SJérôme Duval 			new BMessage(RECORD), new BMessage(RECORD_PERIOD));
30957e2f323SJérôme Duval 		background->AddChild(fRecordButton);
31057e2f323SJérôme Duval 
31157e2f323SJérôme Duval 		//	Button for saving selected sound
31257e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kDiskButtonSize);
31357e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-250, 21));
31457e2f323SJérôme Duval 		fSaveButton = new TransportButton(buttonRect, "Save",
31557e2f323SJérôme Duval 			kDiskButtonBitmapsBits, kPressedDiskButtonBitmapsBits, kDisabledDiskButtonBitmapsBits, new BMessage(SAVE));
31657e2f323SJérôme Duval 		fSaveButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
31757e2f323SJérôme Duval 		background->AddChild(fSaveButton);
31857e2f323SJérôme Duval 
319948356deSJérôme Duval 		//	Button Loop
320948356deSJérôme Duval 		buttonRect = BRect(BPoint(0,0), kArrowSize);
321948356deSJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(23, 48));
322948356deSJérôme Duval 		fLoopButton = new DrawButton(buttonRect, "Loop",
323948356deSJérôme Duval 			kLoopArrowBits, kArrowBits, new BMessage(LOOP));
324948356deSJérôme Duval 		fLoopButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
325948356deSJérôme Duval 		fLoopButton->SetTarget(this);
326948356deSJérôme Duval 		background->AddChild(fLoopButton);
327948356deSJérôme Duval 
32857e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kSpeakerIconBitmapSize);
32957e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(121, 17));
33057e2f323SJérôme Duval 		SpeakerView *speakerView = new SpeakerView(buttonRect, B_FOLLOW_LEFT | B_FOLLOW_TOP);
33157e2f323SJérôme Duval 		speakerView->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
33257e2f323SJérôme Duval 		background->AddChild(speakerView);
33357e2f323SJérôme Duval 
33457e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), BPoint(84, 19));
33557e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(107, 20));
33657e2f323SJérôme Duval 		fVolumeSlider = new VolumeSlider(buttonRect, "volumeSlider", B_FOLLOW_LEFT | B_FOLLOW_TOP);
33757e2f323SJérôme Duval 		fVolumeSlider->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
33857e2f323SJérôme Duval 		background->AddChild(fVolumeSlider);
33957e2f323SJérôme Duval 
34057e2f323SJérôme Duval 		// Button to mask/see sounds list
34157e2f323SJérôme Duval 		buttonRect = BRect(BPoint(0,0), kUpDownButtonSize);
34257e2f323SJérôme Duval 		buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(21, 25));
34357e2f323SJérôme Duval 		fUpDownButton = new UpDownButton(buttonRect, new BMessage(VIEW_LIST));
34457e2f323SJérôme Duval 		fUpDownButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP);
34557e2f323SJérôme Duval 		background->AddChild(fUpDownButton);
34657e2f323SJérôme Duval 
34757e2f323SJérôme Duval 		r = Bounds();
34857e2f323SJérôme Duval 		r.top = background->Bounds().bottom + 1;
34957e2f323SJérôme Duval 		fBottomBox = new BBox(r, "bottomBox", B_FOLLOW_ALL);
35057e2f323SJérôme Duval 		fBottomBox->SetBorder(B_NO_BORDER);
35157e2f323SJérôme Duval 		AddChild(fBottomBox);
35257e2f323SJérôme Duval 
35357e2f323SJérôme Duval 		//	The actual list of recorded sounds (initially empty) sits
35457e2f323SJérôme Duval 		//	below the header with the controls.
35557e2f323SJérôme Duval 		r = fBottomBox->Bounds();
35657e2f323SJérôme Duval 		r.left += 190;
35757e2f323SJérôme Duval 		r.InsetBy(10, 10);
35857e2f323SJérôme Duval 		r.left -= 10;
35957e2f323SJérôme Duval 		r.top += 4;
36057e2f323SJérôme Duval 		r.right -= B_V_SCROLL_BAR_WIDTH;
36157e2f323SJérôme Duval 		r.bottom -= 25;
36257e2f323SJérôme Duval 		fSoundList = new SoundListView(r, "Sound List", B_FOLLOW_ALL);
36357e2f323SJérôme Duval 		fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED));
36457e2f323SJérôme Duval 		fSoundList->SetViewColor(216, 216, 216);
36557e2f323SJérôme Duval 		BScrollView *scroller = new BScrollView("scroller", fSoundList, B_FOLLOW_ALL,
36657e2f323SJérôme Duval 			0, false, true, B_FANCY_BORDER);
36757e2f323SJérôme Duval 		fBottomBox->AddChild(scroller);
36857e2f323SJérôme Duval 
36957e2f323SJérôme Duval 		r = fBottomBox->Bounds();
37057e2f323SJérôme Duval 		r.right = r.left + 190;
37157e2f323SJérôme Duval 		r.bottom -= 25;
37257e2f323SJérôme Duval 		r.InsetBy(10, 8);
37357e2f323SJérôme Duval 		r.top -= 1;
37457e2f323SJérôme Duval 		fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
37557e2f323SJérôme Duval 		fFileInfoBox->SetLabel("File Info");
37657e2f323SJérôme Duval 
37757e2f323SJérôme Duval 		r = fFileInfoBox->Bounds();
37857e2f323SJérôme Duval 		r.left = 8;
37957e2f323SJérôme Duval 		r.top = 13;
38057e2f323SJérôme Duval 		r.bottom = r.top + 15;
38157e2f323SJérôme Duval 		r.right -= 10;
38257e2f323SJérôme Duval 		fFilename = new BStringView(r, "filename", "File Name:");
38357e2f323SJérôme Duval 		fFileInfoBox->AddChild(fFilename);
38457e2f323SJérôme Duval 		r.top += 13;
38557e2f323SJérôme Duval 		r.bottom = r.top + 15;
38657e2f323SJérôme Duval 		fFormat = new BStringView(r, "format", "Format:");
38757e2f323SJérôme Duval 		fFileInfoBox->AddChild(fFormat);
38857e2f323SJérôme Duval 		r.top += 13;
38957e2f323SJérôme Duval 		r.bottom = r.top + 15;
39057e2f323SJérôme Duval 		fCompression = new BStringView(r, "compression", "Compression:");
39157e2f323SJérôme Duval 		fFileInfoBox->AddChild(fCompression);
39257e2f323SJérôme Duval 		r.top += 13;
39357e2f323SJérôme Duval 		r.bottom = r.top + 15;
39457e2f323SJérôme Duval 		fChannels = new BStringView(r, "channels", "Channels:");
39557e2f323SJérôme Duval 		fFileInfoBox->AddChild(fChannels);
39657e2f323SJérôme Duval 		r.top += 13;
39757e2f323SJérôme Duval 		r.bottom = r.top + 15;
39857e2f323SJérôme Duval 		fSampleSize = new BStringView(r, "samplesize", "Sample Size:");
39957e2f323SJérôme Duval 		fFileInfoBox->AddChild(fSampleSize);
40057e2f323SJérôme Duval 		r.top += 13;
40157e2f323SJérôme Duval 		r.bottom = r.top + 15;
40257e2f323SJérôme Duval 		fSampleRate = new BStringView(r, "samplerate", "Sample Rate:");
40357e2f323SJérôme Duval 		fFileInfoBox->AddChild(fSampleRate);
40457e2f323SJérôme Duval 		r.top += 13;
40557e2f323SJérôme Duval 		r.bottom = r.top + 15;
40657e2f323SJérôme Duval 		fDuration = new BStringView(r, "duration", "Duration:");
40757e2f323SJérôme Duval 		fFileInfoBox->AddChild(fDuration);
40857e2f323SJérôme Duval 
40957e2f323SJérôme Duval 		//	Input selection lists all available physical inputs that produce
41057e2f323SJérôme Duval 		//	buffers with B_MEDIA_RAW_AUDIO format data.
41157e2f323SJérôme Duval 		popup = new BPopUpMenu("Input");
41257e2f323SJérôme Duval 		int max_input_count = 64;
41357e2f323SJérôme Duval 		dormant_node_info dni[max_input_count];
41457e2f323SJérôme Duval 
41557e2f323SJérôme Duval 		int32 real_count = max_input_count;
41657e2f323SJérôme Duval 		media_format output_format;
41757e2f323SJérôme Duval 		output_format.type = B_MEDIA_RAW_AUDIO;
41857e2f323SJérôme Duval 		output_format.u.raw_audio = media_raw_audio_format::wildcard;
41957e2f323SJérôme Duval 		error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format,
42057e2f323SJérôme Duval 			0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT);
42157e2f323SJérôme Duval 		if (real_count > max_input_count) {
42257e2f323SJérôme Duval 			WINDOW((stderr, "dropped %ld inputs\n", real_count - max_input_count));
42357e2f323SJérôme Duval 			real_count = max_input_count;
42457e2f323SJérôme Duval 		}
42557e2f323SJérôme Duval 		char selected_name[B_MEDIA_NAME_LENGTH] = "Default Input";
42657e2f323SJérôme Duval 		BMessage * msg;
42757e2f323SJérôme Duval 		BMenuItem * item;
42857e2f323SJérôme Duval 		for (int ix=0; ix<real_count; ix++) {
42957e2f323SJérôme Duval 			msg = new BMessage(INPUT_SELECTED);
43057e2f323SJérôme Duval 			msg->AddData("node", B_RAW_TYPE, &dni[ix], sizeof(dni[ix]));
43157e2f323SJérôme Duval 			item = new BMenuItem(dni[ix].name, msg);
43257e2f323SJérôme Duval 			popup->AddItem(item);
43357e2f323SJérôme Duval 			media_node_id ni[12];
43457e2f323SJérôme Duval 			int32 ni_count = 12;
43557e2f323SJérôme Duval 			error = fRoster->GetInstancesFor(dni[ix].addon, dni[ix].flavor_id, ni, &ni_count);
43657e2f323SJérôme Duval 			if (error == B_OK)
43757e2f323SJérôme Duval 				for (int iy=0; iy<ni_count; iy++)
43857e2f323SJérôme Duval 					if (ni[iy] == fAudioInputNode.node) {
43957e2f323SJérôme Duval 						strcpy(selected_name, dni[ix].name);
44057e2f323SJérôme Duval 						break;
44157e2f323SJérôme Duval 					}
44257e2f323SJérôme Duval 		}
44357e2f323SJérôme Duval 
44457e2f323SJérôme Duval 		//	Create the actual widget
44557e2f323SJérôme Duval 		BRect frame(fBottomBox->Bounds());
44657e2f323SJérôme Duval 		r = frame;
44757e2f323SJérôme Duval 		r.left = 42;
44857e2f323SJérôme Duval 		r.right = (r.left + r.right) / 2;
44957e2f323SJérôme Duval 		r.InsetBy(10,10);
45057e2f323SJérôme Duval 		r.top = r.bottom - 18;
45157e2f323SJérôme Duval 		fInputField = new BMenuField(r, "Input", "Input:", popup);
45280cfc30cSJérôme Duval 		fInputField->SetDivider(fInputField->StringWidth("Input:") + 4.0f);
45357e2f323SJérôme Duval 		fBottomBox->AddChild(fInputField);
45457e2f323SJérôme Duval 
45557e2f323SJérôme Duval 		fBottomBox->AddChild(fFileInfoBox);
45657e2f323SJérôme Duval 
45757e2f323SJérôme Duval 		fBottomBox->Hide();
45857e2f323SJérôme Duval 		CalcSizes(Frame().Width(), MIN_HEIGHT-161);
45957e2f323SJérôme Duval 		ResizeTo(Frame().Width(), MIN_HEIGHT-161);
46057e2f323SJérôme Duval 
46157e2f323SJérôme Duval 
46257e2f323SJérôme Duval 		popup->Superitem()->SetLabel(selected_name);
46357e2f323SJérôme Duval 
46457e2f323SJérôme Duval 		// Make sure the save panel is happy.
4655fa77532SJérôme Duval 		fSavePanel = new BFilePanel(B_SAVE_PANEL);
4665fa77532SJérôme Duval 		fSavePanel->SetTarget(this);
46757e2f323SJérôme Duval 	}
46857e2f323SJérôme Duval 	catch (...) {
46957e2f323SJérôme Duval 		goto bad_mojo;
47057e2f323SJérôme Duval 	}
47157e2f323SJérôme Duval 	UpdateButtons();
47257e2f323SJérôme Duval 	return B_OK;
47357e2f323SJérôme Duval 
47457e2f323SJérôme Duval 	//	Error handling.
47557e2f323SJérôme Duval bad_mojo:
47657e2f323SJérôme Duval 	if (error >= 0) {
47757e2f323SJérôme Duval 		error = B_ERROR;
47857e2f323SJérôme Duval 	}
47957e2f323SJérôme Duval 	if (fRecordNode) {
48057e2f323SJérôme Duval 		fRecordNode->Release();
48157e2f323SJérôme Duval 	}
48257e2f323SJérôme Duval 
48357e2f323SJérôme Duval 	delete fPlayer;
48457e2f323SJérôme Duval 	if (!fInputField) {
48557e2f323SJérôme Duval 		delete popup;
48657e2f323SJérôme Duval 	}
48757e2f323SJérôme Duval 	return error;
48857e2f323SJérôme Duval }
48957e2f323SJérôme Duval 
49057e2f323SJérôme Duval 
49157e2f323SJérôme Duval bool
49257e2f323SJérôme Duval RecorderWindow::QuitRequested()	//	this means Close pressed
49357e2f323SJérôme Duval {
49457e2f323SJérôme Duval 	StopRecording();
49557e2f323SJérôme Duval 	StopPlaying();
49657e2f323SJérôme Duval 	be_app->PostMessage(B_QUIT_REQUESTED);
49757e2f323SJérôme Duval 	return true;
49857e2f323SJérôme Duval }
49957e2f323SJérôme Duval 
50057e2f323SJérôme Duval 
50157e2f323SJérôme Duval void
50257e2f323SJérôme Duval RecorderWindow::MessageReceived(BMessage * message)
50357e2f323SJérôme Duval {
50457e2f323SJérôme Duval 	//	Your average generic message dispatching switch() statement.
50557e2f323SJérôme Duval 	switch (message->what) {
50657e2f323SJérôme Duval 	case INPUT_SELECTED:
50757e2f323SJérôme Duval 		Input(message);
50857e2f323SJérôme Duval 		break;
50957e2f323SJérôme Duval 	case SOUND_SELECTED:
51057e2f323SJérôme Duval 		Selected(message);
51157e2f323SJérôme Duval 		break;
51257e2f323SJérôme Duval 	case STOP_PLAYING:
51357e2f323SJérôme Duval 		StopPlaying();
51457e2f323SJérôme Duval 		break;
51557e2f323SJérôme Duval 	case STOP_RECORDING:
51657e2f323SJérôme Duval 		StopRecording();
51757e2f323SJérôme Duval 		break;
51857e2f323SJérôme Duval 	case PLAY_PERIOD:
51957e2f323SJérôme Duval 		if (fPlayer) {
52057e2f323SJérôme Duval 			if (fPlayer->HasData())
52157e2f323SJérôme Duval 				fPlayButton->SetPlaying();
52257e2f323SJérôme Duval 			else
52357e2f323SJérôme Duval 				fPlayButton->SetPaused();
52457e2f323SJérôme Duval 		}
52557e2f323SJérôme Duval 		break;
52657e2f323SJérôme Duval 	case RECORD_PERIOD:
52757e2f323SJérôme Duval 		fRecordButton->SetRecording();
52857e2f323SJérôme Duval 		break;
52957e2f323SJérôme Duval 	case RECORD:
53057e2f323SJérôme Duval 		Record(message);
53157e2f323SJérôme Duval 		break;
53257e2f323SJérôme Duval 	case STOP:
53357e2f323SJérôme Duval 		Stop(message);
53457e2f323SJérôme Duval 		break;
53557e2f323SJérôme Duval 	case PLAY:
53657e2f323SJérôme Duval 		Play(message);
53757e2f323SJérôme Duval 		break;
53857e2f323SJérôme Duval 	case SAVE:
53957e2f323SJérôme Duval 		Save(message);
54057e2f323SJérôme Duval 		break;
54157e2f323SJérôme Duval 	case B_SAVE_REQUESTED:
54257e2f323SJérôme Duval 		DoSave(message);
54357e2f323SJérôme Duval 		break;
54457e2f323SJérôme Duval 	case VIEW_LIST:
54557e2f323SJérôme Duval 		if (fUpDownButton->Value() == B_CONTROL_ON) {
54657e2f323SJérôme Duval 			fBottomBox->Show();
54757e2f323SJérôme Duval 			CalcSizes(Frame().Width(), MIN_HEIGHT);
54857e2f323SJérôme Duval 			ResizeTo(Frame().Width(), MIN_HEIGHT);
54957e2f323SJérôme Duval 		} else {
55057e2f323SJérôme Duval 			fBottomBox->Hide();
55157e2f323SJérôme Duval 			CalcSizes(Frame().Width(), MIN_HEIGHT-161);
55257e2f323SJérôme Duval 			ResizeTo(Frame().Width(), MIN_HEIGHT-161);
55357e2f323SJérôme Duval 
55457e2f323SJérôme Duval 		}
55557e2f323SJérôme Duval 		break;
55657e2f323SJérôme Duval 	case UPDATE_TRACKSLIDER:
55757e2f323SJérôme Duval 		{
55857e2f323SJérôme Duval 			bigtime_t timestamp = fPlayTrack->CurrentTime();
55957e2f323SJérôme Duval 			fTrackSlider->SetMainTime(timestamp, false);
56057e2f323SJérôme Duval 			fScopeView->SetMainTime(timestamp);
56157e2f323SJérôme Duval 		}
56257e2f323SJérôme Duval 		break;
56357e2f323SJérôme Duval 	case POSITION_CHANGED:
56457e2f323SJérôme Duval 		{
56557e2f323SJérôme Duval 		bigtime_t right, left, main;
56657e2f323SJérôme Duval 		if (message->FindInt64("main", &main) == B_OK) {
56757e2f323SJérôme Duval 			if (fPlayTrack) {
56857e2f323SJérôme Duval 				fPlayTrack->SeekToTime(fTrackSlider->MainTime());
56957e2f323SJérôme Duval 				fPlayFrame = fPlayTrack->CurrentFrame();
57057e2f323SJérôme Duval 			}
57157e2f323SJérôme Duval 			fScopeView->SetMainTime(main);
57257e2f323SJérôme Duval 		}
57357e2f323SJérôme Duval 		if (message->FindInt64("right", &right) == B_OK) {
57457e2f323SJérôme Duval 			if (fPlayTrack)
5757942339dSJérôme Duval 				fPlayLimit = MIN(fPlayFrames, (off_t)(right * fPlayFormat.u.raw_audio.frame_rate/1000000LL));
57657e2f323SJérôme Duval 			fScopeView->SetRightTime(right);
57757e2f323SJérôme Duval 		}
57857e2f323SJérôme Duval 		if (message->FindInt64("left", &left) == B_OK) {
57957e2f323SJérôme Duval 			fScopeView->SetLeftTime(left);
58057e2f323SJérôme Duval 		}
58157e2f323SJérôme Duval 		}
58257e2f323SJérôme Duval 		break;
583948356deSJérôme Duval 	case LOOP:
584948356deSJérôme Duval 		fLooping = fLoopButton->ButtonState();
585948356deSJérôme Duval 		break;
58657e2f323SJérôme Duval 	case B_SIMPLE_DATA:
58757e2f323SJérôme Duval 	case B_REFS_RECEIVED:
58857e2f323SJérôme Duval 		{
58957e2f323SJérôme Duval 			RefsReceived(message);
59057e2f323SJérôme Duval 			break;
59157e2f323SJérôme Duval 		}
592019ed09bSJérôme Duval 	case B_COPY_TARGET:
593019ed09bSJérôme Duval 		CopyTarget(message);
594019ed09bSJérôme Duval 		break;
59557e2f323SJérôme Duval 	default:
59657e2f323SJérôme Duval 		BWindow::MessageReceived(message);
59757e2f323SJérôme Duval 		break;
59857e2f323SJérôme Duval 	}
59957e2f323SJérôme Duval }
60057e2f323SJérôme Duval 
60157e2f323SJérôme Duval 
60257e2f323SJérôme Duval void
60357e2f323SJérôme Duval RecorderWindow::Record(BMessage * message)
60457e2f323SJérôme Duval {
60557e2f323SJérôme Duval 	//	User pressed Record button
60657e2f323SJérôme Duval 	fRecording = true;
60757e2f323SJérôme Duval 	if (fButtonState != btnPaused) {
60857e2f323SJérôme Duval 		StopRecording();
60957e2f323SJérôme Duval 		return;			//	user is too fast on the mouse
61057e2f323SJérôme Duval 	}
61157e2f323SJérôme Duval 	SetButtonState(btnRecording);
61257e2f323SJérôme Duval 	fRecordButton->SetRecording();
61357e2f323SJérôme Duval 
61457e2f323SJérôme Duval 	char name[256];
61557e2f323SJérôme Duval 	//	Create a file with a temporary name
61657e2f323SJérôme Duval 	status_t err = NewTempName(name);
61757e2f323SJérôme Duval 	if (err < B_OK) {
61857e2f323SJérôme Duval 		ErrorAlert("find an unused name to use for the new recording", err);
61957e2f323SJérôme Duval 		return;
62057e2f323SJérôme Duval 	}
62157e2f323SJérôme Duval 	//	Find the file so we can refer to it later
62257e2f323SJérôme Duval 	err = fTempDir.FindEntry(name, &fRecEntry);
62357e2f323SJérôme Duval 	if (err < B_OK) {
62457e2f323SJérôme Duval 		ErrorAlert("find the temporary file created to hold the new recording", err);
62557e2f323SJérôme Duval 		return;
62657e2f323SJérôme Duval 	}
62757e2f323SJérôme Duval 	err = fRecFile.SetTo(&fTempDir, name, O_RDWR);
62857e2f323SJérôme Duval 	if (err < B_OK) {
62957e2f323SJérôme Duval 		ErrorAlert("open the temporary file created to hold the new recording", err);
63057e2f323SJérôme Duval 		fRecEntry.Unset();
63157e2f323SJérôme Duval 		return;
63257e2f323SJérôme Duval 	}
63357e2f323SJérôme Duval 	//	Reserve space on disk (creates fewer fragments)
63407db0c6fSJérôme Duval 	err = fRecFile.SetSize(4 * fRecordFormat.u.raw_audio.channel_count
63588fef303SJérôme Duval 		* fRecordFormat.u.raw_audio.frame_rate * (fRecordFormat.u.raw_audio.format & 0xf));
63657e2f323SJérôme Duval 	if (err < B_OK) {
63757e2f323SJérôme Duval 		ErrorAlert("record a sound that long", err);
63857e2f323SJérôme Duval 		fRecEntry.Remove();
63957e2f323SJérôme Duval 		fRecEntry.Unset();
64057e2f323SJérôme Duval 		return;
64157e2f323SJérôme Duval 	}
64257e2f323SJérôme Duval 	fRecSize = 0;
64357e2f323SJérôme Duval 
64457e2f323SJérôme Duval 	fRecFile.Seek(sizeof(struct wave_struct), SEEK_SET);
64557e2f323SJérôme Duval 
64657e2f323SJérôme Duval 	//	Hook up input
64757e2f323SJérôme Duval 	err = MakeRecordConnection(fAudioInputNode);
64857e2f323SJérôme Duval 	if (err < B_OK) {
64957e2f323SJérôme Duval 		ErrorAlert("connect to the selected sound input", err);
65057e2f323SJérôme Duval 		fRecEntry.Remove();
65157e2f323SJérôme Duval 		fRecEntry.Unset();
65257e2f323SJérôme Duval 		return;
65357e2f323SJérôme Duval 	}
65457e2f323SJérôme Duval 
65557e2f323SJérôme Duval 	//	And get it going...
65657e2f323SJérôme Duval 	bigtime_t then = fRecordNode->TimeSource()->Now()+50000LL;
65757e2f323SJérôme Duval 	fRoster->StartNode(fRecordNode->Node(), then);
65857e2f323SJérôme Duval 	if (fAudioInputNode.kind & B_TIME_SOURCE) {
65957e2f323SJérôme Duval 		fRoster->StartNode(fAudioInputNode, fRecordNode->TimeSource()->RealTimeFor(then, 0));
66057e2f323SJérôme Duval 	}
66157e2f323SJérôme Duval 	else {
66257e2f323SJérôme Duval 		fRoster->StartNode(fAudioInputNode, then);
66357e2f323SJérôme Duval 	}
66457e2f323SJérôme Duval }
66557e2f323SJérôme Duval 
66657e2f323SJérôme Duval void
66757e2f323SJérôme Duval RecorderWindow::Play(BMessage * message)
66857e2f323SJérôme Duval {
66957e2f323SJérôme Duval 	if (fPlayer) {
67057e2f323SJérôme Duval 		//	User pressed Play button and playing
67157e2f323SJérôme Duval 		if (fPlayer->HasData())
67257e2f323SJérôme Duval 			fPlayButton->SetPaused();
67357e2f323SJérôme Duval 		else
67457e2f323SJérôme Duval 			fPlayButton->SetPlaying();
67557e2f323SJérôme Duval 		fPlayer->SetHasData(!fPlayer->HasData());
67657e2f323SJérôme Duval 		return;
67757e2f323SJérôme Duval 	}
67857e2f323SJérôme Duval 
67957e2f323SJérôme Duval 	SetButtonState(btnPlaying);
68057e2f323SJérôme Duval 	fPlayButton->SetPlaying();
68157e2f323SJérôme Duval 
68257e2f323SJérôme Duval 	if (!fPlayTrack) {
68357e2f323SJérôme Duval 		ErrorAlert("get the file to play", B_ERROR);
68457e2f323SJérôme Duval 		return;
68557e2f323SJérôme Duval 	}
68657e2f323SJérôme Duval 
6877942339dSJérôme Duval 	fPlayLimit = MIN(fPlayFrames, (off_t)(fTrackSlider->RightTime() * fPlayFormat.u.raw_audio.frame_rate / 1000000LL));
68857e2f323SJérôme Duval 	fPlayTrack->SeekToTime(fTrackSlider->MainTime());
68957e2f323SJérôme Duval 	fPlayFrame = fPlayTrack->CurrentFrame();
69057e2f323SJérôme Duval 
69157e2f323SJérôme Duval 	// Create our internal Node which plays sound, and register it.
69257e2f323SJérôme Duval 	fPlayer = new BSoundPlayer(fAudioMixerNode, &fPlayFormat.u.raw_audio, "Sound Player");
69357e2f323SJérôme Duval 	status_t err = fPlayer->InitCheck();
69457e2f323SJérôme Duval 	if (err < B_OK) {
69557e2f323SJérôme Duval 		return;
69657e2f323SJérôme Duval 	}
69757e2f323SJérôme Duval 
69857e2f323SJérôme Duval 	fVolumeSlider->SetSoundPlayer(fPlayer);
69957e2f323SJérôme Duval 	fPlayer->SetCallbacks(PlayFile, NotifyPlayFile, this);
70057e2f323SJérôme Duval 
70157e2f323SJérôme Duval 	//	And get it going...
70257e2f323SJérôme Duval 	fPlayer->Start();
70357e2f323SJérôme Duval 	fPlayer->SetHasData(true);
70457e2f323SJérôme Duval }
70557e2f323SJérôme Duval 
70657e2f323SJérôme Duval void
70757e2f323SJérôme Duval RecorderWindow::Stop(BMessage * message)
70857e2f323SJérôme Duval {
70957e2f323SJérôme Duval 	//	User pressed Stop button.
71057e2f323SJérôme Duval 	//	Stop recorder.
71157e2f323SJérôme Duval 	StopRecording();
71257e2f323SJérôme Duval 	//	Stop player.
71357e2f323SJérôme Duval 	StopPlaying();
71457e2f323SJérôme Duval }
71557e2f323SJérôme Duval 
71657e2f323SJérôme Duval void
71757e2f323SJérôme Duval RecorderWindow::Save(BMessage * message)
71857e2f323SJérôme Duval {
71957e2f323SJérôme Duval 	//	User pressed Save button.
72057e2f323SJérôme Duval 	//	Find the item to save.
72157e2f323SJérôme Duval 	int32 index = fSoundList->CurrentSelection();
72257e2f323SJérôme Duval 	SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(index));
72357e2f323SJérôme Duval 	if ((! pItem) || (pItem->Entry().InitCheck() != B_OK)) {
72457e2f323SJérôme Duval 		return;
72557e2f323SJérôme Duval 	}
72657e2f323SJérôme Duval 
72757e2f323SJérôme Duval 	// Update the save panel and show it.
72857e2f323SJérôme Duval 	char filename[B_FILE_NAME_LENGTH];
72957e2f323SJérôme Duval 	pItem->Entry().GetName(filename);
73057e2f323SJérôme Duval 	BMessage saveMsg(B_SAVE_REQUESTED);
73157e2f323SJérôme Duval 	entry_ref ref;
73257e2f323SJérôme Duval 	pItem->Entry().GetRef(&ref);
73357e2f323SJérôme Duval 
734*a4797804SJérôme Duval 	if (saveMsg.AddPointer("sound list item", pItem) != B_OK)
735*a4797804SJérôme Duval 		fprintf(stderr, "failed to add pItem\n");
7365fa77532SJérôme Duval 	fSavePanel->SetSaveText(filename);
7375fa77532SJérôme Duval 	fSavePanel->SetMessage(&saveMsg);
7385fa77532SJérôme Duval 	fSavePanel->Show();
73957e2f323SJérôme Duval }
74057e2f323SJérôme Duval 
74157e2f323SJérôme Duval void
74257e2f323SJérôme Duval RecorderWindow::DoSave(BMessage * message)
74357e2f323SJérôme Duval {
74457e2f323SJérôme Duval 	// User picked a place to put the file.
74557e2f323SJérôme Duval 	// Find the location of the old (e.g.
74657e2f323SJérôme Duval 	// temporary file), and the name of the
74757e2f323SJérôme Duval 	// new file to save.
74857e2f323SJérôme Duval 	entry_ref old_ref, new_dir_ref;
74957e2f323SJérôme Duval 	const char* new_name;
75057e2f323SJérôme Duval 	SoundListItem* pItem;
75157e2f323SJérôme Duval 
75257e2f323SJérôme Duval 	if ((message->FindPointer("sound list item", (void**) &pItem) == B_OK)
75357e2f323SJérôme Duval 		&& (message->FindRef("directory", &new_dir_ref) == B_OK)
75457e2f323SJérôme Duval 		&& (message->FindString("name", &new_name) == B_OK))
75557e2f323SJérôme Duval 	{
75657e2f323SJérôme Duval 		BEntry& oldEntry = pItem->Entry();
75757e2f323SJérôme Duval 		BFile oldFile(&oldEntry, B_READ_WRITE);
75857e2f323SJérôme Duval 		if (oldFile.InitCheck() != B_OK)
75957e2f323SJérôme Duval 			return;
76057e2f323SJérôme Duval 
76157e2f323SJérôme Duval 		BDirectory newDir(&new_dir_ref);
76257e2f323SJérôme Duval 		if (newDir.InitCheck() != B_OK)
76357e2f323SJérôme Duval 			return;
76457e2f323SJérôme Duval 
76557e2f323SJérôme Duval 		BFile newFile;
76657e2f323SJérôme Duval 		newDir.CreateFile(new_name, &newFile);
76757e2f323SJérôme Duval 
76857e2f323SJérôme Duval 		if (newFile.InitCheck() != B_OK)
76957e2f323SJérôme Duval 			return;
77057e2f323SJérôme Duval 
77157e2f323SJérôme Duval 		status_t err = CopyFile(newFile, oldFile);
77257e2f323SJérôme Duval 
77357e2f323SJérôme Duval 		if (err == B_OK) {
77457e2f323SJérôme Duval 			// clean up the sound list and item
77557e2f323SJérôme Duval 			if (pItem->IsTemp())
77657e2f323SJérôme Duval 				oldEntry.Remove(); // blows away temp file!
77757e2f323SJérôme Duval 			oldEntry.SetTo(&newDir, new_name);
77857e2f323SJérôme Duval 			pItem->SetTemp(false);	// don't blow the new entry away when we exit!
77957e2f323SJérôme Duval 			fSoundList->Invalidate();
78057e2f323SJérôme Duval 		}
78157e2f323SJérôme Duval 	} else {
78257e2f323SJérôme Duval 		WINDOW((stderr, "Couldn't save file.\n"));
78357e2f323SJérôme Duval 	}
78457e2f323SJérôme Duval }
78557e2f323SJérôme Duval 
78657e2f323SJérôme Duval 
78757e2f323SJérôme Duval void
78857e2f323SJérôme Duval RecorderWindow::Input(BMessage * message)
78957e2f323SJérôme Duval {
79057e2f323SJérôme Duval 	//	User selected input from pop-up
79157e2f323SJérôme Duval 	const dormant_node_info * dni = 0;
79257e2f323SJérôme Duval 	ssize_t size = 0;
79357e2f323SJérôme Duval 	if (message->FindData("node", B_RAW_TYPE, (const void **)&dni, &size)) {
79457e2f323SJérôme Duval 		return;		//	bad input selection message
79557e2f323SJérôme Duval 	}
79657e2f323SJérôme Duval 
79757e2f323SJérôme Duval 	media_node_id node_id;
79857e2f323SJérôme Duval 	status_t error = fRoster->GetInstancesFor(dni->addon, dni->flavor_id, &node_id);
79957e2f323SJérôme Duval 	if (error != B_OK) {
80057e2f323SJérôme Duval 		fRoster->InstantiateDormantNode(*dni, &fAudioInputNode);
80157e2f323SJérôme Duval 	} else {
80257e2f323SJérôme Duval 		fRoster->GetNodeFor(node_id, &fAudioInputNode);
80357e2f323SJérôme Duval 	}
80457e2f323SJérôme Duval }
80557e2f323SJérôme Duval 
80657e2f323SJérôme Duval void
80757e2f323SJérôme Duval RecorderWindow::Selected(BMessage * message)
80857e2f323SJérôme Duval {
80957e2f323SJérôme Duval 	//	User selected a sound in list view
810c6f8aa29SJérôme Duval 	int32 selIdx = fSoundList->CurrentSelection();
811c6f8aa29SJérôme Duval 	SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(selIdx));
812c6f8aa29SJérôme Duval 	if (!pItem)
813c6f8aa29SJérôme Duval 		return;
814c6f8aa29SJérôme Duval 	status_t err = UpdatePlayFile(pItem, true);
815c6f8aa29SJérôme Duval 	if (err != B_OK) {
816c6f8aa29SJérôme Duval 		ErrorAlert("recognize this file as a media file", err == B_MEDIA_NO_HANDLER ? B_OK : err);
817c6f8aa29SJérôme Duval 		RemoveCurrentSoundItem();
818c6f8aa29SJérôme Duval 	}
81957e2f323SJérôme Duval 	UpdateButtons();
82057e2f323SJérôme Duval }
82157e2f323SJérôme Duval 
82257e2f323SJérôme Duval status_t
82357e2f323SJérôme Duval RecorderWindow::MakeRecordConnection(const media_node & input)
82457e2f323SJérôme Duval {
82557e2f323SJérôme Duval 	CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n"));
82657e2f323SJérôme Duval 
82757e2f323SJérôme Duval 	//	Find an available output for the given input node.
82857e2f323SJérôme Duval 	int32 count = 0;
82957e2f323SJérôme Duval 	status_t err = fRoster->GetFreeOutputsFor(input, &fAudioOutput, 1, &count, B_MEDIA_RAW_AUDIO);
83057e2f323SJérôme Duval 	if (err < B_OK) {
83157e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't get free outputs from audio input node\n"));
83257e2f323SJérôme Duval 		return err;
83357e2f323SJérôme Duval 	}
83457e2f323SJérôme Duval 	if (count < 1) {
83557e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): no free outputs from audio input node\n"));
83657e2f323SJérôme Duval 		return B_BUSY;
83757e2f323SJérôme Duval 	}
83857e2f323SJérôme Duval 
83957e2f323SJérôme Duval 	//	Find an available input for our own Node. Note that we go through the
84057e2f323SJérôme Duval 	//	MediaRoster; calling Media Kit methods directly on Nodes in our app is
84157e2f323SJérôme Duval 	//	not OK (because synchronization happens in the service thread, not in
84257e2f323SJérôme Duval 	//	the calling thread).
84357e2f323SJérôme Duval 	// TODO: explain this
84457e2f323SJérôme Duval 	err = fRoster->GetFreeInputsFor(fRecordNode->Node(), &fRecInput, 1, &count, B_MEDIA_RAW_AUDIO);
84557e2f323SJérôme Duval 	if (err < B_OK) {
84657e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't get free inputs for sound recorder\n"));
84757e2f323SJérôme Duval 		return err;
84857e2f323SJérôme Duval 	}
84957e2f323SJérôme Duval 	if (count < 1) {
85057e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): no free inputs for sound recorder\n"));
85157e2f323SJérôme Duval 		return B_BUSY;
85257e2f323SJérôme Duval 	}
85357e2f323SJérôme Duval 
85457e2f323SJérôme Duval 	//	Find out what the time source of the input is.
85557e2f323SJérôme Duval 	//	For most nodes, we just use the preferred time source (the DAC) for synchronization.
85657e2f323SJérôme Duval 	//	However, nodes that record from an input need to synchronize to the audio input node
85757e2f323SJérôme Duval 	//	instead for best results.
85857e2f323SJérôme Duval 	//	MakeTimeSourceFor gives us a "clone" of the time source node that we can manipulate
85957e2f323SJérôme Duval 	//	to our heart's content. When we're done with it, though, we need to call Release()
86057e2f323SJérôme Duval 	//	on the time source node, so that it keeps an accurate reference count and can delete
86157e2f323SJérôme Duval 	//	itself when it's no longer needed.
86257e2f323SJérôme Duval 	// TODO: what about filters connected to audio input?
86357e2f323SJérôme Duval 	media_node use_time_source;
86457e2f323SJérôme Duval 	BTimeSource * tsobj = fRoster->MakeTimeSourceFor(input);
86557e2f323SJérôme Duval 	if (! tsobj) {
86657e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't clone time source from audio input node\n"));
86757e2f323SJérôme Duval 		return B_MEDIA_BAD_NODE;
86857e2f323SJérôme Duval 	}
86957e2f323SJérôme Duval 
87057e2f323SJérôme Duval 	//	Apply the time source in effect to our own Node.
87157e2f323SJérôme Duval 	err = fRoster->SetTimeSourceFor(fRecordNode->Node().node, tsobj->Node().node);
87257e2f323SJérôme Duval 	if (err < B_OK) {
87357e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't set the sound recorder's time source\n"));
87457e2f323SJérôme Duval 		tsobj->Release();
87557e2f323SJérôme Duval 		return err;
87657e2f323SJérôme Duval 	}
87757e2f323SJérôme Duval 
87857e2f323SJérôme Duval 	//	Get a format, any format.
87988fef303SJérôme Duval 	fRecordFormat.u.raw_audio = fAudioOutput.format.u.raw_audio;
88088fef303SJérôme Duval 	fRecordFormat.type = B_MEDIA_RAW_AUDIO;
88157e2f323SJérôme Duval 
88257e2f323SJérôme Duval 	//	Tell the consumer where we want data to go.
88357e2f323SJérôme Duval 	err = fRecordNode->SetHooks(RecordFile, NotifyRecordFile, this);
88457e2f323SJérôme Duval 	if (err < B_OK) {
88557e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): couldn't set the sound recorder's hook functions\n"));
88657e2f323SJérôme Duval 		tsobj->Release();
88757e2f323SJérôme Duval 		return err;
88857e2f323SJérôme Duval 	}
88957e2f323SJérôme Duval 
89057e2f323SJérôme Duval 	//	Using the same structs for input and output is OK in BMediaRoster::Connect().
89188fef303SJérôme Duval 	err = fRoster->Connect(fAudioOutput.source, fRecInput.destination, &fRecordFormat, &fAudioOutput, &fRecInput);
89257e2f323SJérôme Duval 	if (err < B_OK) {
89357e2f323SJérôme Duval 		CONNECT((stderr, "RecorderWindow::MakeRecordConnection(): failed to connect sound recorder to audio input node.\n"));
89457e2f323SJérôme Duval 		tsobj->Release();
89557e2f323SJérôme Duval 		fRecordNode->SetHooks(0, 0, 0);
89657e2f323SJérôme Duval 		return err;
89757e2f323SJérôme Duval 	}
89857e2f323SJérôme Duval 
89957e2f323SJérôme Duval 	//	Start the time source if it's not running.
90057e2f323SJérôme Duval 	if ((tsobj->Node() != input) && !tsobj->IsRunning()) {
90157e2f323SJérôme Duval 		fRoster->StartNode(tsobj->Node(), BTimeSource::RealTime());
90257e2f323SJérôme Duval 	}
90357e2f323SJérôme Duval 	tsobj->Release();	//	we're done with this time source instance!
90457e2f323SJérôme Duval 	return B_OK;
90557e2f323SJérôme Duval }
90657e2f323SJérôme Duval 
90757e2f323SJérôme Duval 
90857e2f323SJérôme Duval status_t
90957e2f323SJérôme Duval RecorderWindow::BreakRecordConnection()
91057e2f323SJérôme Duval {
91157e2f323SJérôme Duval 	status_t err;
91257e2f323SJérôme Duval 
91357e2f323SJérôme Duval 	//	If we are the last connection, the Node will stop automatically since it
91457e2f323SJérôme Duval 	//	has nowhere to send data to.
91557e2f323SJérôme Duval 	err = fRoster->StopNode(fRecInput.node, 0);
91657e2f323SJérôme Duval 	err = fRoster->Disconnect(fAudioOutput.node.node, fAudioOutput.source, fRecInput.node.node, fRecInput.destination);
91757e2f323SJérôme Duval 	fAudioOutput.source = media_source::null;
91857e2f323SJérôme Duval 	fRecInput.destination = media_destination::null;
91957e2f323SJérôme Duval 	return err;
92057e2f323SJérôme Duval }
92157e2f323SJérôme Duval 
92257e2f323SJérôme Duval status_t
92357e2f323SJérôme Duval RecorderWindow::StopRecording()
92457e2f323SJérôme Duval {
92557e2f323SJérôme Duval 	if (!fRecording)
92657e2f323SJérôme Duval 		return B_OK;
92757e2f323SJérôme Duval 	fRecording = false;
92857e2f323SJérôme Duval 	BreakRecordConnection();
92957e2f323SJérôme Duval 	fRecordNode->SetHooks(NULL,NULL,NULL);
93057e2f323SJérôme Duval 	if (fRecSize > 0) {
93157e2f323SJérôme Duval 
93257e2f323SJérôme Duval 		wave_struct header;
93357e2f323SJérôme Duval 		header.riff.riff_id = FOURCC('R','I','F','F');
934*a4797804SJérôme Duval 		header.riff.len = fRecSize + sizeof(header) - 8;
93557e2f323SJérôme Duval 		header.riff.wave_id = FOURCC('W','A','V','E');
93657e2f323SJérôme Duval 		header.format_chunk.fourcc = FOURCC('f','m','t',' ');
93757e2f323SJérôme Duval 		header.format_chunk.len = sizeof(header.format);
93857e2f323SJérôme Duval 		header.format.format_tag = 1;
93988fef303SJérôme Duval 		header.format.channels = fRecordFormat.u.raw_audio.channel_count;
94088fef303SJérôme Duval 		header.format.samples_per_sec = (uint32)fRecordFormat.u.raw_audio.frame_rate;
94188fef303SJérôme Duval 		header.format.avg_bytes_per_sec = (uint32)(fRecordFormat.u.raw_audio.frame_rate
94288fef303SJérôme Duval 			* fRecordFormat.u.raw_audio.channel_count
94388fef303SJérôme Duval 			* (fRecordFormat.u.raw_audio.format & 0xf));
94488fef303SJérôme Duval 		header.format.bits_per_sample = (fRecordFormat.u.raw_audio.format & 0xf) * 8;
94588fef303SJérôme Duval 		header.format.block_align = (fRecordFormat.u.raw_audio.format & 0xf)
94688fef303SJérôme Duval 			* fRecordFormat.u.raw_audio.channel_count;
94757e2f323SJérôme Duval 		header.data_chunk.fourcc = FOURCC('d','a','t','a');
94857e2f323SJérôme Duval 		header.data_chunk.len = fRecSize;
94957e2f323SJérôme Duval 		fRecFile.Seek(0, SEEK_SET);
95057e2f323SJérôme Duval 		fRecFile.Write(&header, sizeof(header));
95157e2f323SJérôme Duval 
95257e2f323SJérôme Duval 		fRecFile.SetSize(fRecSize + sizeof(header));	//	We reserve space; make sure we cut off any excess at the end.
95357e2f323SJérôme Duval 		AddSoundItem(fRecEntry, true);
95457e2f323SJérôme Duval 	}
95557e2f323SJérôme Duval 	else {
95657e2f323SJérôme Duval 		fRecEntry.Remove();
95757e2f323SJérôme Duval 	}
95857e2f323SJérôme Duval 	//	We're done for this time.
95957e2f323SJérôme Duval 	fRecEntry.Unset();
96057e2f323SJérôme Duval 	//	Close the file.
96157e2f323SJérôme Duval 	fRecFile.Unset();
96257e2f323SJérôme Duval 	//	No more recording going on.
96357e2f323SJérôme Duval 	fRecSize = 0;
96457e2f323SJérôme Duval 	SetButtonState(btnPaused);
96557e2f323SJérôme Duval 	fRecordButton->SetStopped();
96657e2f323SJérôme Duval 
96757e2f323SJérôme Duval 	return B_OK;
96857e2f323SJérôme Duval }
96957e2f323SJérôme Duval 
97057e2f323SJérôme Duval 
97157e2f323SJérôme Duval status_t
97257e2f323SJérôme Duval RecorderWindow::StopPlaying()
97357e2f323SJérôme Duval {
97457e2f323SJérôme Duval 	if (fPlayer) {
97557e2f323SJérôme Duval 		fPlayer->Stop();
97657e2f323SJérôme Duval 		fPlayer->SetCallbacks(0, 0, 0);
97757e2f323SJérôme Duval 		fVolumeSlider->SetSoundPlayer(NULL);
97857e2f323SJérôme Duval 		delete fPlayer;
97957e2f323SJérôme Duval 		fPlayer = NULL;
98057e2f323SJérôme Duval 	}
98157e2f323SJérôme Duval 	SetButtonState(btnPaused);
98257e2f323SJérôme Duval 	fPlayButton->SetStopped();
98357e2f323SJérôme Duval 	fTrackSlider->ResetMainTime();
98457e2f323SJérôme Duval 	fScopeView->SetMainTime(*fTrackSlider->MainTime());
98557e2f323SJérôme Duval 	return B_OK;
98657e2f323SJérôme Duval }
98757e2f323SJérôme Duval 
98857e2f323SJérôme Duval 
98957e2f323SJérôme Duval void
99057e2f323SJérôme Duval RecorderWindow::SetButtonState(BtnState state)
99157e2f323SJérôme Duval {
99257e2f323SJérôme Duval 	fButtonState = state;
99357e2f323SJérôme Duval 	UpdateButtons();
99457e2f323SJérôme Duval }
99557e2f323SJérôme Duval 
99657e2f323SJérôme Duval 
99757e2f323SJérôme Duval void
99857e2f323SJérôme Duval RecorderWindow::UpdateButtons()
99957e2f323SJérôme Duval {
100057e2f323SJérôme Duval 	bool hasSelection = (fSoundList->CurrentSelection() >= 0);
100157e2f323SJérôme Duval 	fRecordButton->SetEnabled(fButtonState != btnPlaying);
100257e2f323SJérôme Duval 	fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
100357e2f323SJérôme Duval 	fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
100457e2f323SJérôme Duval 	fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection);
100557e2f323SJérôme Duval 	fStopButton->SetEnabled(fButtonState != btnPaused);
100657e2f323SJérôme Duval 	fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording));
100757e2f323SJérôme Duval 	fInputField->SetEnabled(fButtonState != btnRecording);
100857e2f323SJérôme Duval }
100957e2f323SJérôme Duval 
10105fa77532SJérôme Duval #ifndef __HAIKU__
10115fa77532SJérôme Duval extern "C" status_t DecodedFormat__11BMediaTrackP12media_format(BMediaTrack *self, media_format *inout_format);
10125fa77532SJérôme Duval #endif
101357e2f323SJérôme Duval 
1014c6f8aa29SJérôme Duval status_t
1015c6f8aa29SJérôme Duval RecorderWindow::UpdatePlayFile(SoundListItem* item, bool updateDisplay)
101657e2f323SJérôme Duval {
1017019ed09bSJérôme Duval 	fScopeView->CancelRendering();
1018019ed09bSJérôme Duval 	StopPlaying();
1019019ed09bSJérôme Duval 	StopRecording();
1020019ed09bSJérôme Duval 
1021dbbc34b6SJérôme Duval 	if (fPlayTrack && fPlayFile) {
10227942339dSJérôme Duval 		fPlayFile->ReleaseTrack(fPlayTrack);
10237942339dSJérôme Duval 		fPlayTrack = NULL;
1024dbbc34b6SJérôme Duval 	}
1025dbbc34b6SJérôme Duval 	if (fPlayFile) {
1026dbbc34b6SJérôme Duval 		delete fPlayFile;
10277942339dSJérôme Duval 		fPlayFile = NULL;
1028dbbc34b6SJérôme Duval 	}
10297942339dSJérôme Duval 
103057e2f323SJérôme Duval 	status_t err;
1031c6f8aa29SJérôme Duval 	BEntry& entry = item->Entry();
103257e2f323SJérôme Duval 	entry_ref ref;
103357e2f323SJérôme Duval 	entry.GetRef(&ref);
103457e2f323SJérôme Duval 	fPlayFile = new BMediaFile(&ref); //, B_MEDIA_FILE_UNBUFFERED);
103557e2f323SJérôme Duval 	if ((err = fPlayFile->InitCheck()) < B_OK) {
103657e2f323SJérôme Duval 		delete fPlayFile;
1037dbbc34b6SJérôme Duval 		fPlayFile = NULL;
1038c6f8aa29SJérôme Duval 		return err;
103957e2f323SJérôme Duval 	}
104057e2f323SJérôme Duval 
104157e2f323SJérôme Duval 	for (int ix=0; ix<fPlayFile->CountTracks(); ix++) {
104257e2f323SJérôme Duval 		BMediaTrack * track = fPlayFile->TrackAt(ix);
104357e2f323SJérôme Duval 		fPlayFormat.type = B_MEDIA_RAW_AUDIO;
10445fa77532SJérôme Duval #ifdef __HAIKU__
10455fa77532SJérôme Duval 		if ((track->DecodedFormat(&fPlayFormat) == B_OK)
10465fa77532SJérôme Duval #else
10475fa77532SJérôme Duval 		if ((DecodedFormat__11BMediaTrackP12media_format(track, &fPlayFormat) == B_OK)
10485fa77532SJérôme Duval #endif
10495fa77532SJérôme Duval 			&& (fPlayFormat.type == B_MEDIA_RAW_AUDIO)) {
105057e2f323SJérôme Duval 			fPlayTrack = track;
105157e2f323SJérôme Duval 			break;
105257e2f323SJérôme Duval 		}
105357e2f323SJérôme Duval 		if (track)
105457e2f323SJérôme Duval 			fPlayFile->ReleaseTrack(track);
105557e2f323SJérôme Duval 	}
105657e2f323SJérôme Duval 
105757e2f323SJérôme Duval 	if (!fPlayTrack) {
105857e2f323SJérôme Duval 		delete fPlayFile;
1059019ed09bSJérôme Duval 		fPlayFile = NULL;
1060c6f8aa29SJérôme Duval 		return B_STREAM_NOT_FOUND;
106157e2f323SJérôme Duval 	}
106257e2f323SJérôme Duval 
1063c6f8aa29SJérôme Duval 	if (!updateDisplay)
1064c6f8aa29SJérôme Duval 		return B_OK;
1065c6f8aa29SJérôme Duval 
106657e2f323SJérôme Duval 	BString filename = "File Name: ";
106757e2f323SJérôme Duval 	filename << ref.name;
106857e2f323SJérôme Duval 	fFilename->SetText(filename.String());
106957e2f323SJérôme Duval 
107057e2f323SJérôme Duval 	BString format = "Format: ";
107157e2f323SJérôme Duval 	media_file_format file_format;
107257e2f323SJérôme Duval 	if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK)
107357e2f323SJérôme Duval 		format << file_format.short_name;
107457e2f323SJérôme Duval 	BString compression = "Compression: ";
107557e2f323SJérôme Duval 	media_codec_info codec_info;
107657e2f323SJérôme Duval 	if (fPlayTrack->GetCodecInfo(&codec_info) == B_OK) {
107757e2f323SJérôme Duval 		if (strcmp(codec_info.short_name, "raw")==0)
107857e2f323SJérôme Duval 			compression << "None";
107957e2f323SJérôme Duval 		else
108057e2f323SJérôme Duval 			compression << codec_info.short_name;
108157e2f323SJérôme Duval 	}
108257e2f323SJérôme Duval 	BString channels = "Channels: ";
108357e2f323SJérôme Duval 	channels << fPlayFormat.u.raw_audio.channel_count;
108457e2f323SJérôme Duval 	BString samplesize = "Sample Size: ";
108557e2f323SJérôme Duval 	samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf) << " bits";
108657e2f323SJérôme Duval 	BString samplerate = "Sample Rate: ";
108757e2f323SJérôme Duval 	samplerate << (int)fPlayFormat.u.raw_audio.frame_rate;
108857e2f323SJérôme Duval 	BString durationString = "Duration: ";
108957e2f323SJérôme Duval 	bigtime_t duration = fPlayTrack->Duration();
109057e2f323SJérôme Duval 	durationString << (float)(duration / 1000000.0) << " seconds";
109157e2f323SJérôme Duval 
109257e2f323SJérôme Duval 	fFormat->SetText(format.String());
109357e2f323SJérôme Duval 	fCompression->SetText(compression.String());
109457e2f323SJérôme Duval 	fChannels->SetText(channels.String());
109557e2f323SJérôme Duval 	fSampleSize->SetText(samplesize.String());
109657e2f323SJérôme Duval 	fSampleRate->SetText(samplerate.String());
109757e2f323SJérôme Duval 	fDuration->SetText(durationString.String());
109857e2f323SJérôme Duval 
109957e2f323SJérôme Duval 	fTrackSlider->SetTotalTime(duration, true);
1100019ed09bSJérôme Duval 	fScopeView->SetTotalTime(duration, true);
110157e2f323SJérôme Duval 	fScopeView->RenderTrack(fPlayTrack, fPlayFormat);
11027942339dSJérôme Duval 
11037942339dSJérôme Duval 	fPlayFrames = fPlayTrack->CountFrames();
1104c6f8aa29SJérôme Duval 	return B_OK;
110557e2f323SJérôme Duval }
110657e2f323SJérôme Duval 
110757e2f323SJérôme Duval 
110857e2f323SJérôme Duval void
110957e2f323SJérôme Duval RecorderWindow::ErrorAlert(const char * action, status_t err)
111057e2f323SJérôme Duval {
111157e2f323SJérôme Duval 	char msg[300];
1112c6f8aa29SJérôme Duval 	if (err != B_OK)
11135fa77532SJérôme Duval 		sprintf(msg, "Cannot %s: %s. [%lx]", action, strerror(err), (int32) err);
1114c6f8aa29SJérôme Duval 	else
1115c6f8aa29SJérôme Duval 		sprintf(msg, "Cannot %s.", action);
111657e2f323SJérôme Duval 	(new BAlert("", msg, "Stop"))->Go();
111757e2f323SJérôme Duval }
111857e2f323SJérôme Duval 
111957e2f323SJérôme Duval 
112057e2f323SJérôme Duval status_t
112157e2f323SJérôme Duval RecorderWindow::NewTempName(char * name)
112257e2f323SJérôme Duval {
112357e2f323SJérôme Duval 	int init_count = fTempCount;
112457e2f323SJérôme Duval again:
112557e2f323SJérôme Duval 	if (fTempCount-init_count > 25) {
112657e2f323SJérôme Duval 		return B_ERROR;
112757e2f323SJérôme Duval 	}
112857e2f323SJérôme Duval 	else {
112957e2f323SJérôme Duval 		fTempCount++;
113057e2f323SJérôme Duval 		if (fTempCount==0)
113157e2f323SJérôme Duval 			sprintf(name, "Audio Clip");
113257e2f323SJérôme Duval 		else
113357e2f323SJérôme Duval 			sprintf(name, "Audio Clip %d", fTempCount);
113457e2f323SJérôme Duval 		BPath path;
113557e2f323SJérôme Duval 		status_t err;
113657e2f323SJérôme Duval 		BEntry tempEnt;
113757e2f323SJérôme Duval 		if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) {
113857e2f323SJérôme Duval 			return err;
113957e2f323SJérôme Duval 		}
114057e2f323SJérôme Duval 		if ((err = tempEnt.GetPath(&path)) < B_OK) {
114157e2f323SJérôme Duval 			return err;
114257e2f323SJérôme Duval 		}
114357e2f323SJérôme Duval 		path.Append(name);
114457e2f323SJérôme Duval 		int fd;
114557e2f323SJérôme Duval 		//	Use O_EXCL so we know we created the file (sync with other instances)
114657e2f323SJérôme Duval 		if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
114757e2f323SJérôme Duval 			goto again;
114857e2f323SJérôme Duval 		}
114957e2f323SJérôme Duval 		close(fd);
115057e2f323SJérôme Duval 	}
115157e2f323SJérôme Duval 	return B_OK;
115257e2f323SJérôme Duval }
115357e2f323SJérôme Duval 
115457e2f323SJérôme Duval 
115557e2f323SJérôme Duval void
115657e2f323SJérôme Duval RecorderWindow::AddSoundItem(const BEntry& entry, bool temp)
115757e2f323SJérôme Duval {
115857e2f323SJérôme Duval 	//	Create list item to display.
115957e2f323SJérôme Duval 	SoundListItem * listItem = new SoundListItem(entry, temp);
116057e2f323SJérôme Duval 	fSoundList->AddItem(listItem);
116157e2f323SJérôme Duval 	fSoundList->Invalidate();
116257e2f323SJérôme Duval 	fSoundList->Select(fSoundList->IndexOf(listItem));
116357e2f323SJérôme Duval }
116457e2f323SJérôme Duval 
1165019ed09bSJérôme Duval 
1166019ed09bSJérôme Duval void
1167019ed09bSJérôme Duval RecorderWindow::RemoveCurrentSoundItem() {
1168c6f8aa29SJérôme Duval 	int32 index = fSoundList->CurrentSelection();
1169c6f8aa29SJérôme Duval 	BListItem *item = fSoundList->RemoveItem(index);
1170019ed09bSJérôme Duval 	delete item;
1171c6f8aa29SJérôme Duval 	if (index >= fSoundList->CountItems())
1172c6f8aa29SJérôme Duval 		index = fSoundList->CountItems() - 1;
1173c6f8aa29SJérôme Duval 	fSoundList->Select(index);
1174019ed09bSJérôme Duval }
1175019ed09bSJérôme Duval 
1176019ed09bSJérôme Duval 
117757e2f323SJérôme Duval void
1178ef367a8aSStephan Aßmus RecorderWindow::RecordFile(void * cookie, bigtime_t timestamp,
1179ef367a8aSStephan Aßmus 	void * data, size_t size, const media_raw_audio_format & format)
118057e2f323SJérôme Duval {
118157e2f323SJérôme Duval 	//	Callback called from the SoundConsumer when receiving buffers.
118257e2f323SJérôme Duval 	RecorderWindow * window = (RecorderWindow *)cookie;
118357e2f323SJérôme Duval 
118457e2f323SJérôme Duval 	if (window->fRecording) {
118557e2f323SJérôme Duval 		//	Write the data to file (we don't buffer or guard file access
118657e2f323SJérôme Duval 		//	or anything)
118757e2f323SJérôme Duval 		window->fRecFile.WriteAt(window->fRecSize, data, size);
118857e2f323SJérôme Duval 		window->fVUView->ComputeNextLevel(data, size);
118957e2f323SJérôme Duval 		window->fRecSize += size;
119057e2f323SJérôme Duval 	}
119157e2f323SJérôme Duval }
119257e2f323SJérôme Duval 
119357e2f323SJérôme Duval 
119457e2f323SJérôme Duval void
119557e2f323SJérôme Duval RecorderWindow::NotifyRecordFile(void * cookie, int32 code, ...)
119657e2f323SJérôme Duval {
119757e2f323SJérôme Duval 	if ((code == B_WILL_STOP) || (code == B_NODE_DIES)) {
119857e2f323SJérôme Duval 		RecorderWindow * window = (RecorderWindow *)cookie;
119957e2f323SJérôme Duval 		// Tell the window we've stopped, if it doesn't
120057e2f323SJérôme Duval 		// already know.
120157e2f323SJérôme Duval 		window->PostMessage(STOP_RECORDING);
120257e2f323SJérôme Duval 	}
120357e2f323SJérôme Duval }
120457e2f323SJérôme Duval 
120557e2f323SJérôme Duval 
120657e2f323SJérôme Duval void
120757e2f323SJérôme Duval RecorderWindow::PlayFile(void * cookie, void * data, size_t size, const media_raw_audio_format & format)
120857e2f323SJérôme Duval {
120957e2f323SJérôme Duval 	//	Callback called from the SoundProducer when producing buffers.
121057e2f323SJérôme Duval 	RecorderWindow * window = (RecorderWindow *)cookie;
121157e2f323SJérôme Duval 	int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) *
121257e2f323SJérôme Duval 		window->fPlayFormat.u.raw_audio.channel_count;
121357e2f323SJérôme Duval 
1214948356deSJérôme Duval 	if ((window->fPlayFrame < window->fPlayLimit) || window->fLooping) {
1215948356deSJérôme Duval 		if (window->fPlayFrame >= window->fPlayLimit) {
1216948356deSJérôme Duval 			bigtime_t left = window->fTrackSlider->LeftTime();
1217948356deSJérôme Duval 			window->fPlayTrack->SeekToTime(&left);
1218948356deSJérôme Duval 			window->fPlayFrame = window->fPlayTrack->CurrentFrame();
1219948356deSJérôme Duval 		}
122057e2f323SJérôme Duval 		int64 frames = 0;
122157e2f323SJérôme Duval 		window->fPlayTrack->ReadFrames(data, &frames);
122257e2f323SJérôme Duval 		window->fVUView->ComputeNextLevel(data, size/frame_size);
122357e2f323SJérôme Duval 		window->fPlayFrame += size/frame_size;
122457e2f323SJérôme Duval 		window->PostMessage(UPDATE_TRACKSLIDER);
122557e2f323SJérôme Duval 	} else {
122657e2f323SJérôme Duval 		//	we're done!
122757e2f323SJérôme Duval 		window->PostMessage(STOP_PLAYING);
122857e2f323SJérôme Duval 	}
122957e2f323SJérôme Duval }
123057e2f323SJérôme Duval 
123157e2f323SJérôme Duval void
123257e2f323SJérôme Duval RecorderWindow::NotifyPlayFile(void * cookie, BSoundPlayer::sound_player_notification code, ...)
123357e2f323SJérôme Duval {
123457e2f323SJérôme Duval 	if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) {
123557e2f323SJérôme Duval 		RecorderWindow * window = (RecorderWindow *)cookie;
123657e2f323SJérôme Duval 		// tell the window we've stopped, if it doesn't
123757e2f323SJérôme Duval 		// already know.
123857e2f323SJérôme Duval 		window->PostMessage(STOP_PLAYING);
123957e2f323SJérôme Duval 	}
124057e2f323SJérôme Duval }
124157e2f323SJérôme Duval 
124257e2f323SJérôme Duval 
124357e2f323SJérôme Duval void
124457e2f323SJérôme Duval RecorderWindow::RefsReceived(BMessage *msg)
124557e2f323SJérôme Duval {
124657e2f323SJérôme Duval 	entry_ref ref;
124757e2f323SJérôme Duval 	int32 i = 0;
1248c6f8aa29SJérôme Duval 	int32 countGood = 0;
1249c6f8aa29SJérôme Duval 	int32 countBad = 0;
125057e2f323SJérôme Duval 
125157e2f323SJérôme Duval 	while (msg->FindRef("refs", i++, &ref) == B_OK) {
125257e2f323SJérôme Duval 
125357e2f323SJérôme Duval 		BEntry entry(&ref, true);
125457e2f323SJérôme Duval 		BPath path(&entry);
125557e2f323SJérôme Duval 		BNode node(&entry);
125657e2f323SJérôme Duval 
125757e2f323SJérôme Duval 		if (node.IsFile()) {
1258c6f8aa29SJérôme Duval 			SoundListItem * listItem = new SoundListItem(entry, false);
1259c6f8aa29SJérôme Duval 			if (UpdatePlayFile(listItem) == B_OK) {
1260c6f8aa29SJérôme Duval 				fSoundList->AddItem(listItem);
1261c6f8aa29SJérôme Duval 				countGood++;
1262c6f8aa29SJérôme Duval 				continue;
1263c6f8aa29SJérôme Duval 			}
1264c6f8aa29SJérôme Duval 			delete listItem;
126557e2f323SJérôme Duval 		} else if(node.IsDirectory()) {
126657e2f323SJérôme Duval 
126757e2f323SJérôme Duval 		}
1268c6f8aa29SJérôme Duval 		countBad++;
1269c6f8aa29SJérôme Duval 	}
1270c6f8aa29SJérôme Duval 
1271c6f8aa29SJérôme Duval 	if (countBad > 0 && countGood == 0)
1272c6f8aa29SJérôme Duval 		(new BAlert("Nothing to Play", "None of the files appear to be "
1273c6f8aa29SJérôme Duval 			"audio files", "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go();
1274c6f8aa29SJérôme Duval 	else if (countGood > 0) {
1275c6f8aa29SJérôme Duval 		if (countBad > 0)
1276c6f8aa29SJérôme Duval 			(new BAlert("Invalid audio files", "Some of the files "
1277c6f8aa29SJérôme Duval 				"don't appear to be audio files", "OK", NULL, NULL,
1278c6f8aa29SJérôme Duval 				B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
1279c6f8aa29SJérôme Duval 		fSoundList->Select(fSoundList->CountItems() - 1);
128057e2f323SJérôme Duval 	}
128157e2f323SJérôme Duval }
1282019ed09bSJérôme Duval 
1283019ed09bSJérôme Duval 
1284019ed09bSJérôme Duval void
1285019ed09bSJérôme Duval RecorderWindow::CopyTarget(BMessage *msg)
1286019ed09bSJérôme Duval {
1287019ed09bSJérôme Duval 	const char *type = NULL;
1288019ed09bSJérôme Duval 	if (msg->FindString("be:types", &type) == B_OK) {
1289019ed09bSJérôme Duval 		if (!strcasecmp(type, B_FILE_MIME_TYPE)) {
1290019ed09bSJérôme Duval 			const char *name;
1291019ed09bSJérôme Duval 			entry_ref dir;
1292019ed09bSJérôme Duval 			if (msg->FindString("be:filetypes") == B_OK
1293019ed09bSJérôme Duval 				&& msg->FindString("name", &name) == B_OK
1294019ed09bSJérôme Duval 				&& msg->FindRef("directory", &dir) == B_OK) {
1295019ed09bSJérôme Duval 				BDirectory directory(&dir);
1296019ed09bSJérôme Duval 				BFile file(&directory, name, O_RDWR | O_TRUNC);
1297019ed09bSJérôme Duval 
1298019ed09bSJérôme Duval 				// seek time
1299019ed09bSJérôme Duval 				bigtime_t start = fTrackSlider->LeftTime();
1300019ed09bSJérôme Duval 
1301019ed09bSJérôme Duval 				// write data
1302019ed09bSJérôme Duval 				bigtime_t diffTime = fTrackSlider->RightTime() - fTrackSlider->LeftTime();
1303019ed09bSJérôme Duval 				int64 framesToWrite = (int64) (diffTime * fPlayFormat.u.raw_audio.frame_rate / 1000000LL);
1304019ed09bSJérôme Duval 				int32 frameSize = (fPlayFormat.u.raw_audio.format & 0xf)
1305019ed09bSJérôme Duval 					* fPlayFormat.u.raw_audio.channel_count;
1306019ed09bSJérôme Duval 
1307019ed09bSJérôme Duval 				wave_struct header;
1308019ed09bSJérôme Duval 				header.riff.riff_id = FOURCC('R','I','F','F');
1309*a4797804SJérôme Duval 				header.riff.len = (frameSize * framesToWrite) + sizeof(header) - 8;
1310019ed09bSJérôme Duval 				header.riff.wave_id = FOURCC('W','A','V','E');
1311019ed09bSJérôme Duval 				header.format_chunk.fourcc = FOURCC('f','m','t',' ');
1312019ed09bSJérôme Duval 				header.format_chunk.len = sizeof(header.format);
1313019ed09bSJérôme Duval 				header.format.format_tag = 1;
1314019ed09bSJérôme Duval 				header.format.channels = fPlayFormat.u.raw_audio.channel_count;
1315019ed09bSJérôme Duval 				header.format.samples_per_sec = (uint32)fPlayFormat.u.raw_audio.frame_rate;
1316019ed09bSJérôme Duval 				header.format.avg_bytes_per_sec = (uint32)(fPlayFormat.u.raw_audio.frame_rate
1317019ed09bSJérôme Duval 					* fPlayFormat.u.raw_audio.channel_count
1318019ed09bSJérôme Duval 					* (fPlayFormat.u.raw_audio.format & 0xf));
1319019ed09bSJérôme Duval 				header.format.bits_per_sample = (fPlayFormat.u.raw_audio.format & 0xf) * 8;
1320019ed09bSJérôme Duval 				header.format.block_align = frameSize;
1321019ed09bSJérôme Duval 				header.data_chunk.fourcc = FOURCC('d','a','t','a');
1322019ed09bSJérôme Duval 				header.data_chunk.len = frameSize * framesToWrite;
1323019ed09bSJérôme Duval 				file.Seek(0, SEEK_SET);
1324019ed09bSJérôme Duval 				file.Write(&header, sizeof(header));
1325019ed09bSJérôme Duval 
1326019ed09bSJérôme Duval 				char *data = (char *)malloc(fPlayFormat.u.raw_audio.buffer_size);
1327019ed09bSJérôme Duval 
1328c6f8aa29SJérôme Duval 				fPlayTrack->SeekToTime(&start);
1329c6f8aa29SJérôme Duval 				fPlayFrame = fPlayTrack->CurrentFrame();
1330019ed09bSJérôme Duval 				while (framesToWrite > 0) {
1331019ed09bSJérôme Duval 					int64 frames = 0;
1332019ed09bSJérôme Duval 					status_t err = fPlayTrack->ReadFrames(data, &frames);
1333c6f8aa29SJérôme Duval 					if (frames <= 0 || err != B_OK) {
1334c6f8aa29SJérôme Duval 						if (err != B_OK)
1335c6f8aa29SJérôme Duval 							fprintf(stderr, "CopyTarget: ReadFrames failed\n");
1336019ed09bSJérôme Duval 						break;
1337c6f8aa29SJérôme Duval 					}
1338019ed09bSJérôme Duval 					file.Write(data, frames * frameSize);
1339019ed09bSJérôme Duval 					framesToWrite -= frames;
1340019ed09bSJérôme Duval 				}
1341019ed09bSJérôme Duval 
1342019ed09bSJérôme Duval 				file.Sync();
1343019ed09bSJérôme Duval 				free(data);
1344019ed09bSJérôme Duval 				BNodeInfo nodeInfo(&file);
1345019ed09bSJérôme Duval 				// set type
1346019ed09bSJérôme Duval 			}
1347019ed09bSJérôme Duval 		} else {
1348019ed09bSJérôme Duval 
1349019ed09bSJérôme Duval 		}
1350019ed09bSJérôme Duval 	}
1351019ed09bSJérôme Duval }
1352