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 }