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