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