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