157e2f323SJérôme Duval /* 257e2f323SJérôme Duval * Copyright 2005, Jérôme Duval. All rights reserved. 3*739fd34cSBarrett * Copyright 2014, Dario Casalinuovo. All rights reserved. 457e2f323SJérôme Duval * Distributed under the terms of the MIT License. 557e2f323SJérôme Duval * 6ad0c65eaSJérôme Duval * Inspired by SoundCapture from Be newsletter (Media Kit Basics: 7ad0c65eaSJérôme Duval * Consumers and Producers) 857e2f323SJérôme Duval */ 957e2f323SJérôme Duval 1057e2f323SJérôme Duval #include <Application.h> 1157e2f323SJérôme Duval #include <Alert.h> 1257e2f323SJérôme Duval #include <Debug.h> 1357e2f323SJérôme Duval #include <Screen.h> 1457e2f323SJérôme Duval #include <Button.h> 1557e2f323SJérôme Duval #include <CheckBox.h> 1657e2f323SJérôme Duval #include <TextControl.h> 1757e2f323SJérôme Duval #include <MenuField.h> 1857e2f323SJérôme Duval #include <PopUpMenu.h> 1957e2f323SJérôme Duval #include <MenuItem.h> 2057e2f323SJérôme Duval #include <Box.h> 2157e2f323SJérôme Duval #include <ScrollView.h> 2257e2f323SJérôme Duval #include <Beep.h> 2357e2f323SJérôme Duval #include <StringView.h> 2457e2f323SJérôme Duval #include <String.h> 2557e2f323SJérôme Duval #include <Slider.h> 2657e2f323SJérôme Duval #include <Message.h> 2757e2f323SJérôme Duval 2857e2f323SJérôme Duval #include <Path.h> 2957e2f323SJérôme Duval #include <FindDirectory.h> 3057e2f323SJérôme Duval #include <MediaAddOn.h> 3157e2f323SJérôme Duval 3257e2f323SJérôme Duval #include <SoundPlayer.h> 3357e2f323SJérôme Duval 3457e2f323SJérôme Duval #include <assert.h> 3557e2f323SJérôme Duval #include <stdio.h> 363aeed660SJérôme Duval #include <strings.h> 3757e2f323SJérôme Duval #include <stdlib.h> 3857e2f323SJérôme Duval #include <ctype.h> 3957e2f323SJérôme Duval #include <unistd.h> 40338b8dc3SIngo Weinhold #include <fcntl.h> 4157e2f323SJérôme Duval 4257e2f323SJérôme Duval #include <MediaRoster.h> 4357e2f323SJérôme Duval #include <TimeSource.h> 44019ed09bSJérôme Duval #include <NodeInfo.h> 4557e2f323SJérôme Duval 46*739fd34cSBarrett #include "SoundUtils.h" 4757e2f323SJérôme Duval #include "RecorderWindow.h" 4857e2f323SJérôme Duval #include "FileUtils.h" 4957e2f323SJérôme Duval 5057e2f323SJérôme Duval #if ! NDEBUG 5157e2f323SJérôme Duval #define FPRINTF(args) fprintf args 5257e2f323SJérôme Duval #else 5357e2f323SJérôme Duval #define FPRINTF(args) 5457e2f323SJérôme Duval #endif 5557e2f323SJérôme Duval 5657e2f323SJérôme Duval #define DEATH FPRINTF 5757e2f323SJérôme Duval #define CONNECT FPRINTF 5857e2f323SJérôme Duval #define WINDOW FPRINTF 5957e2f323SJérôme Duval 60546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT 61546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "RecorderWindow" 623ee96407SJérôme Duval 633ee96407SJérôme Duval 6457e2f323SJérôme Duval // default window positioning 6557e2f323SJérôme Duval static const float MIN_WIDTH = 400.0f; 66b271ff3fSPhilippe Saint-Pierre static const float MIN_HEIGHT = 175.0f; 6757e2f323SJérôme Duval static const float XPOS = 100.0f; 6857e2f323SJérôme Duval static const float YPOS = 200.0f; 6957e2f323SJérôme Duval 70ad0c65eaSJérôme Duval #define FOURCC(a,b,c,d) ((((uint32)(d)) << 24) | (((uint32)(c)) << 16) \ 71ad0c65eaSJérôme Duval | (((uint32)(b)) << 8) | ((uint32)(a))) 7257e2f323SJérôme Duval 7357e2f323SJérôme Duval struct riff_struct 7457e2f323SJérôme Duval { 7557e2f323SJérôme Duval uint32 riff_id; // 'RIFF' 7657e2f323SJérôme Duval uint32 len; 7757e2f323SJérôme Duval uint32 wave_id; // 'WAVE' 7857e2f323SJérôme Duval }; 7957e2f323SJérôme Duval 8057e2f323SJérôme Duval struct chunk_struct 8157e2f323SJérôme Duval { 8257e2f323SJérôme Duval uint32 fourcc; 8357e2f323SJérôme Duval uint32 len; 8457e2f323SJérôme Duval }; 8557e2f323SJérôme Duval 8657e2f323SJérôme Duval struct format_struct 8757e2f323SJérôme Duval { 8857e2f323SJérôme Duval uint16 format_tag; 8957e2f323SJérôme Duval uint16 channels; 9057e2f323SJérôme Duval uint32 samples_per_sec; 9157e2f323SJérôme Duval uint32 avg_bytes_per_sec; 9257e2f323SJérôme Duval uint16 block_align; 9357e2f323SJérôme Duval uint16 bits_per_sample; 9457e2f323SJérôme Duval }; 9557e2f323SJérôme Duval 9657e2f323SJérôme Duval 9757e2f323SJérôme Duval struct wave_struct 9857e2f323SJérôme Duval { 9957e2f323SJérôme Duval struct riff_struct riff; 10057e2f323SJérôme Duval struct chunk_struct format_chunk; 10157e2f323SJérôme Duval struct format_struct format; 10257e2f323SJérôme Duval struct chunk_struct data_chunk; 10357e2f323SJérôme Duval }; 10457e2f323SJérôme Duval 10557e2f323SJérôme Duval 10671302e7cSJonas Sundström RecorderWindow::RecorderWindow() 10771302e7cSJonas Sundström : 10871302e7cSJonas Sundström BWindow(BRect(XPOS, YPOS, XPOS + MIN_WIDTH, YPOS + MIN_HEIGHT), 109560ff447SJonas Sundström B_TRANSLATE_SYSTEM_NAME("SoundRecorder"), B_TITLED_WINDOW, 11071302e7cSJonas Sundström B_ASYNCHRONOUS_CONTROLS | B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE), 11157e2f323SJérôme Duval fPlayer(NULL), 1125fa77532SJérôme Duval fSoundList(NULL), 11357e2f323SJérôme Duval fPlayFile(NULL), 11457e2f323SJérôme Duval fPlayTrack(NULL), 1157942339dSJérôme Duval fPlayFrames(0), 116948356deSJérôme Duval fLooping(false), 1175fa77532SJérôme Duval fSavePanel(NULL), 118cff7eda5SWim van der Meer fInitCheck(B_OK) 11957e2f323SJérôme Duval { 12057e2f323SJérôme Duval fRoster = NULL; 12157e2f323SJérôme Duval fRecordButton = NULL; 12257e2f323SJérôme Duval fPlayButton = NULL; 12357e2f323SJérôme Duval fStopButton = NULL; 12457e2f323SJérôme Duval fSaveButton = NULL; 125948356deSJérôme Duval fLoopButton = NULL; 12657e2f323SJérôme Duval fInputField = NULL; 127*739fd34cSBarrett fRecorder = NULL; 12857e2f323SJérôme Duval fRecording = false; 12957e2f323SJérôme Duval fTempCount = -1; 13057e2f323SJérôme Duval fButtonState = btnPaused; 13157e2f323SJérôme Duval 13257e2f323SJérôme Duval CalcSizes(MIN_WIDTH, MIN_HEIGHT); 13357e2f323SJérôme Duval 1345fa77532SJérôme Duval fInitCheck = InitWindow(); 1355fa77532SJérôme Duval if (fInitCheck != B_OK) { 136bdb1d3acSJérôme Duval if (fInitCheck == B_NAME_NOT_FOUND) 1373ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot find default audio hardware"), 1383ee96407SJérôme Duval fInitCheck); 139bdb1d3acSJérôme Duval else 1403ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot connect to media server"), 1413ee96407SJérôme Duval fInitCheck); 1425fa77532SJérôme Duval PostMessage(B_QUIT_REQUESTED); 1435fa77532SJérôme Duval } else 1445fa77532SJérôme Duval Show(); 14557e2f323SJérôme Duval } 14657e2f323SJérôme Duval 1478e32a27eSPhilippe Saint-Pierre 14857e2f323SJérôme Duval RecorderWindow::~RecorderWindow() 14957e2f323SJérôme Duval { 150*739fd34cSBarrett // The MediaRecorder have to be deleted, the dtor 151*739fd34cSBarrett // disconnect it from the media_kit. 152*739fd34cSBarrett delete fRecorder; 153*739fd34cSBarrett 15457e2f323SJérôme Duval delete fPlayer; 1557942339dSJérôme Duval 1567942339dSJérôme Duval if (fPlayTrack && fPlayFile) 1577942339dSJérôme Duval fPlayFile->ReleaseTrack(fPlayTrack); 158*739fd34cSBarrett 1597942339dSJérôme Duval if (fPlayFile) 1607942339dSJérôme Duval delete fPlayFile; 1617942339dSJérôme Duval fPlayTrack = NULL; 1627942339dSJérôme Duval fPlayFile = NULL; 1637942339dSJérôme Duval 16457e2f323SJérôme Duval // Clean up items in list view. 16557e2f323SJérôme Duval if (fSoundList) { 16657e2f323SJérôme Duval fSoundList->DeselectAll(); 167ad0c65eaSJérôme Duval for (int i = 0; i < fSoundList->CountItems(); i++) { 168ad0c65eaSJérôme Duval WINDOW((stderr, "clean up item %d\n", i+1)); 169ad0c65eaSJérôme Duval SoundListItem* item = dynamic_cast<SoundListItem *>(fSoundList->ItemAt(i)); 17057e2f323SJérôme Duval if (item) { 171b1ed9a63SJérôme Duval if (item->IsTemp()) 17257e2f323SJérôme Duval item->Entry().Remove(); // delete temp file 17357e2f323SJérôme Duval delete item; 17457e2f323SJérôme Duval } 17557e2f323SJérôme Duval } 17657e2f323SJérôme Duval fSoundList->MakeEmpty(); 17757e2f323SJérôme Duval } 17857e2f323SJérôme Duval // Clean up currently recording file, if any. 17957e2f323SJérôme Duval fRecEntry.Remove(); 18057e2f323SJérôme Duval fRecEntry.Unset(); 18105ea8535SKarsten Heimrich 18205ea8535SKarsten Heimrich delete fSavePanel; 18357e2f323SJérôme Duval } 18457e2f323SJérôme Duval 18557e2f323SJérôme Duval 1865fa77532SJérôme Duval status_t 1875fa77532SJérôme Duval RecorderWindow::InitCheck() 1885fa77532SJérôme Duval { 1895fa77532SJérôme Duval return fInitCheck; 1905fa77532SJérôme Duval } 1915fa77532SJérôme Duval 1925fa77532SJérôme Duval 19357e2f323SJérôme Duval void 194b1ed9a63SJérôme Duval RecorderWindow::CalcSizes(float min_width, float min_height) 19557e2f323SJérôme Duval { 19657e2f323SJérôme Duval // Set up size limits based on new screen size 1975e99b7dfSJérôme Duval BScreen screen(this); 198b1ed9a63SJérôme Duval BRect rect = screen.Frame(); 199b1ed9a63SJérôme Duval float width = rect.Width() - 12; 200b1ed9a63SJérôme Duval SetSizeLimits(min_width, width, min_height, rect.Height() - 24); 20157e2f323SJérôme Duval 20257e2f323SJérôme Duval // Don't zoom to cover all of screen; user can resize last bit if necessary. 20357e2f323SJérôme Duval // This leaves other windows visible. 204b1ed9a63SJérôme Duval if (width > 640) 205b1ed9a63SJérôme Duval width = 640 + (width - 640) / 2; 206b1ed9a63SJérôme Duval SetZoomLimits(width, rect.Height() - 24); 20757e2f323SJérôme Duval } 20857e2f323SJérôme Duval 20957e2f323SJérôme Duval 21057e2f323SJérôme Duval status_t 21157e2f323SJérôme Duval RecorderWindow::InitWindow() 21257e2f323SJérôme Duval { 21357e2f323SJérôme Duval BPopUpMenu * popup = 0; 21457e2f323SJérôme Duval status_t error; 21557e2f323SJérôme Duval 21657e2f323SJérôme Duval try { 21757e2f323SJérôme Duval // Find temp directory for recorded sounds. 21857e2f323SJérôme Duval BPath path; 2194b7e2196SIngo Weinhold if (!(error = find_directory(B_SYSTEM_TEMP_DIRECTORY, &path))) 22057e2f323SJérôme Duval error = fTempDir.SetTo(path.Path()); 221b1ed9a63SJérôme Duval if (error < 0) 22257e2f323SJérôme Duval goto bad_mojo; 22357e2f323SJérôme Duval 22457e2f323SJérôme Duval // Make sure the media roster is there (which means the server is there). 22557e2f323SJérôme Duval fRoster = BMediaRoster::Roster(&error); 226b1ed9a63SJérôme Duval if (!fRoster) 22757e2f323SJérôme Duval goto bad_mojo; 22857e2f323SJérôme Duval 22957e2f323SJérôme Duval error = fRoster->GetAudioInput(&fAudioInputNode); 230b1ed9a63SJérôme Duval if (error < B_OK) // there's no input? 23157e2f323SJérôme Duval goto bad_mojo; 23257e2f323SJérôme Duval 23357e2f323SJérôme Duval error = fRoster->GetAudioMixer(&fAudioMixerNode); 234b1ed9a63SJérôme Duval if (error < B_OK) // there's no mixer? 23557e2f323SJérôme Duval goto bad_mojo; 23657e2f323SJérôme Duval 237*739fd34cSBarrett fRecorder = new BMediaRecorder("Sound Recorder", 238*739fd34cSBarrett B_MEDIA_RAW_AUDIO); 239*739fd34cSBarrett 240*739fd34cSBarrett if (fRecorder->InitCheck() < B_OK) 24157e2f323SJérôme Duval goto bad_mojo; 24257e2f323SJérôme Duval 243*739fd34cSBarrett // Set the node to accept only audio data 244*739fd34cSBarrett media_format output_format; 245*739fd34cSBarrett output_format.type = B_MEDIA_RAW_AUDIO; 246*739fd34cSBarrett output_format.u.raw_audio = media_raw_audio_format::wildcard; 247*739fd34cSBarrett fRecorder->SetAcceptedFormat(output_format); 248*739fd34cSBarrett 24957e2f323SJérôme Duval // Create the window header with controls 25057e2f323SJérôme Duval BRect r(Bounds()); 25157e2f323SJérôme Duval r.bottom = r.top + 175; 252*739fd34cSBarrett BBox *background = new BBox(r, "_background", 253*739fd34cSBarrett B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW 254*739fd34cSBarrett | B_FRAME_EVENTS | B_NAVIGABLE_JUMP, B_NO_BORDER); 255*739fd34cSBarrett 25657e2f323SJérôme Duval AddChild(background); 25757e2f323SJérôme Duval 25857e2f323SJérôme Duval r = background->Bounds(); 259a4797804SJérôme Duval r.left = 0; 2608881ad65SJérôme Duval r.right = r.left + 38; 26157e2f323SJérôme Duval r.bottom = r.top + 104; 26257e2f323SJérôme Duval fVUView = new VUView(r, B_FOLLOW_LEFT|B_FOLLOW_TOP); 26357e2f323SJérôme Duval background->AddChild(fVUView); 26457e2f323SJérôme Duval 26557e2f323SJérôme Duval r = background->Bounds(); 2668881ad65SJérôme Duval r.left = r.left + 40; 26757e2f323SJérôme Duval r.bottom = r.top + 104; 26857e2f323SJérôme Duval fScopeView = new ScopeView(r, B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP); 26957e2f323SJérôme Duval background->AddChild(fScopeView); 27057e2f323SJérôme Duval 27157e2f323SJérôme Duval r = background->Bounds(); 27257e2f323SJérôme Duval r.left = 2; 27357e2f323SJérôme Duval r.right -= 26; 27457e2f323SJérôme Duval r.top = 115; 27557e2f323SJérôme Duval r.bottom = r.top + 30; 276ad0c65eaSJérôme Duval fTrackSlider = new TrackSlider(r, "trackSlider", new BMessage(POSITION_CHANGED), 277ad0c65eaSJérôme Duval B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 27857e2f323SJérôme Duval background->AddChild(fTrackSlider); 27957e2f323SJérôme Duval 28057e2f323SJérôme Duval BRect buttonRect; 28157e2f323SJérôme Duval 28257e2f323SJérôme Duval // Button for rewinding 28357e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSkipButtonSize); 28457e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-7, 25)); 2853ee96407SJérôme Duval fRewindButton = new TransportButton(buttonRect, B_TRANSLATE("Rewind"), 286ad0c65eaSJérôme Duval kSkipBackBitmapBits, kPressedSkipBackBitmapBits, 287ad0c65eaSJérôme Duval kDisabledSkipBackBitmapBits, new BMessage(REWIND)); 28857e2f323SJérôme Duval background->AddChild(fRewindButton); 28957e2f323SJérôme Duval 29057e2f323SJérôme Duval // Button for stopping recording or playback 29157e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kStopButtonSize); 29257e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-48, 25)); 2933ee96407SJérôme Duval fStopButton = new TransportButton(buttonRect, B_TRANSLATE("Stop"), 294ad0c65eaSJérôme Duval kStopButtonBitmapBits, kPressedStopButtonBitmapBits, 295ad0c65eaSJérôme Duval kDisabledStopButtonBitmapBits, new BMessage(STOP)); 29657e2f323SJérôme Duval background->AddChild(fStopButton); 29757e2f323SJérôme Duval 29857e2f323SJérôme Duval // Button for starting playback of selected sound 29957e2f323SJérôme Duval BRect playRect(BPoint(0,0), kPlayButtonSize); 30057e2f323SJérôme Duval playRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-82, 25)); 3013ee96407SJérôme Duval fPlayButton = new PlayPauseButton(playRect, B_TRANSLATE("Play"), 30257e2f323SJérôme Duval new BMessage(PLAY), new BMessage(PLAY_PERIOD), ' ', 0); 30357e2f323SJérôme Duval background->AddChild(fPlayButton); 30457e2f323SJérôme Duval 30557e2f323SJérôme Duval // Button for forwarding 30657e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSkipButtonSize); 30757e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-133, 25)); 3083ee96407SJérôme Duval fForwardButton = new TransportButton(buttonRect, B_TRANSLATE("Forward"), 309ad0c65eaSJérôme Duval kSkipForwardBitmapBits, kPressedSkipForwardBitmapBits, 310ad0c65eaSJérôme Duval kDisabledSkipForwardBitmapBits, new BMessage(FORWARD)); 31157e2f323SJérôme Duval background->AddChild(fForwardButton); 31257e2f323SJérôme Duval 31357e2f323SJérôme Duval // Button to start recording (or waiting for sound) 31457e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kRecordButtonSize); 31557e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-174, 25)); 3163ee96407SJérôme Duval fRecordButton = new RecordButton(buttonRect, B_TRANSLATE("Record"), 31757e2f323SJérôme Duval new BMessage(RECORD), new BMessage(RECORD_PERIOD)); 31857e2f323SJérôme Duval background->AddChild(fRecordButton); 31957e2f323SJérôme Duval 32057e2f323SJérôme Duval // Button for saving selected sound 32157e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kDiskButtonSize); 32257e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().LeftBottom() - BPoint(-250, 21)); 3233ee96407SJérôme Duval fSaveButton = new TransportButton(buttonRect, B_TRANSLATE("Save"), 324ad0c65eaSJérôme Duval kDiskButtonBitmapsBits, kPressedDiskButtonBitmapsBits, 325ad0c65eaSJérôme Duval kDisabledDiskButtonBitmapsBits, new BMessage(SAVE)); 32657e2f323SJérôme Duval fSaveButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 32757e2f323SJérôme Duval background->AddChild(fSaveButton); 32857e2f323SJérôme Duval 329948356deSJérôme Duval // Button Loop 330948356deSJérôme Duval buttonRect = BRect(BPoint(0,0), kArrowSize); 331948356deSJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(23, 48)); 3323ee96407SJérôme Duval fLoopButton = new DrawButton(buttonRect, B_TRANSLATE("Loop"), 333948356deSJérôme Duval kLoopArrowBits, kArrowBits, new BMessage(LOOP)); 334948356deSJérôme Duval fLoopButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 335948356deSJérôme Duval fLoopButton->SetTarget(this); 336948356deSJérôme Duval background->AddChild(fLoopButton); 337948356deSJérôme Duval 33857e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kSpeakerIconBitmapSize); 33957e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(121, 17)); 340ad0c65eaSJérôme Duval SpeakerView *speakerView = new SpeakerView(buttonRect, 341ad0c65eaSJérôme Duval B_FOLLOW_LEFT | B_FOLLOW_TOP); 34257e2f323SJérôme Duval speakerView->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 34357e2f323SJérôme Duval background->AddChild(speakerView); 34457e2f323SJérôme Duval 34557e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), BPoint(84, 19)); 34657e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(107, 20)); 347ad0c65eaSJérôme Duval fVolumeSlider = new VolumeSlider(buttonRect, "volumeSlider", 348ad0c65eaSJérôme Duval B_FOLLOW_LEFT | B_FOLLOW_TOP); 34957e2f323SJérôme Duval fVolumeSlider->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 35057e2f323SJérôme Duval background->AddChild(fVolumeSlider); 35157e2f323SJérôme Duval 35257e2f323SJérôme Duval // Button to mask/see sounds list 35357e2f323SJérôme Duval buttonRect = BRect(BPoint(0,0), kUpDownButtonSize); 35457e2f323SJérôme Duval buttonRect.OffsetTo(background->Bounds().RightBottom() - BPoint(21, 25)); 35557e2f323SJérôme Duval fUpDownButton = new UpDownButton(buttonRect, new BMessage(VIEW_LIST)); 35657e2f323SJérôme Duval fUpDownButton->SetResizingMode(B_FOLLOW_RIGHT | B_FOLLOW_TOP); 35757e2f323SJérôme Duval background->AddChild(fUpDownButton); 35857e2f323SJérôme Duval 35957e2f323SJérôme Duval r = Bounds(); 36057e2f323SJérôme Duval r.top = background->Bounds().bottom + 1; 36157e2f323SJérôme Duval fBottomBox = new BBox(r, "bottomBox", B_FOLLOW_ALL); 36257e2f323SJérôme Duval fBottomBox->SetBorder(B_NO_BORDER); 36357e2f323SJérôme Duval AddChild(fBottomBox); 36457e2f323SJérôme Duval 36557e2f323SJérôme Duval // The actual list of recorded sounds (initially empty) sits 36657e2f323SJérôme Duval // below the header with the controls. 36757e2f323SJérôme Duval r = fBottomBox->Bounds(); 36857e2f323SJérôme Duval r.left += 190; 36957e2f323SJérôme Duval r.InsetBy(10, 10); 37057e2f323SJérôme Duval r.left -= 10; 37157e2f323SJérôme Duval r.top += 4; 37257e2f323SJérôme Duval r.right -= B_V_SCROLL_BAR_WIDTH; 37357e2f323SJérôme Duval r.bottom -= 25; 3743ee96407SJérôme Duval fSoundList = new SoundListView(r, B_TRANSLATE("Sound List"), 3753ee96407SJérôme Duval B_FOLLOW_ALL); 37657e2f323SJérôme Duval fSoundList->SetSelectionMessage(new BMessage(SOUND_SELECTED)); 377b271ff3fSPhilippe Saint-Pierre fSoundList->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 378ad0c65eaSJérôme Duval BScrollView *scroller = new BScrollView("scroller", fSoundList, 379ad0c65eaSJérôme Duval B_FOLLOW_ALL, 0, false, true, B_FANCY_BORDER); 38057e2f323SJérôme Duval fBottomBox->AddChild(scroller); 38157e2f323SJérôme Duval 38257e2f323SJérôme Duval r = fBottomBox->Bounds(); 38357e2f323SJérôme Duval r.right = r.left + 190; 38457e2f323SJérôme Duval r.bottom -= 25; 38557e2f323SJérôme Duval r.InsetBy(10, 8); 38657e2f323SJérôme Duval r.top -= 1; 387b271ff3fSPhilippe Saint-Pierre fFileInfoBox = new BBox(r, "fileinfo", B_FOLLOW_LEFT); 3883ee96407SJérôme Duval fFileInfoBox->SetLabel(B_TRANSLATE("File info")); 38957e2f323SJérôme Duval 390b271ff3fSPhilippe Saint-Pierre fFileInfoBox->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 391b271ff3fSPhilippe Saint-Pierre 392b271ff3fSPhilippe Saint-Pierre BFont font = be_plain_font; 393b271ff3fSPhilippe Saint-Pierre font.SetSize(font.Size() * 0.92f); 394b271ff3fSPhilippe Saint-Pierre font_height height; 395b271ff3fSPhilippe Saint-Pierre font.GetHeight(&height); 396b271ff3fSPhilippe Saint-Pierre float fontHeight = height.ascent + height.leading + height.descent; 397b271ff3fSPhilippe Saint-Pierre 39857e2f323SJérôme Duval r = fFileInfoBox->Bounds(); 39957e2f323SJérôme Duval r.left = 8; 400b271ff3fSPhilippe Saint-Pierre r.top = fontHeight + 6; 401b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + fontHeight + 3; 40257e2f323SJérôme Duval r.right -= 10; 4033ee96407SJérôme Duval fFilename = new BStringView(r, "filename", B_TRANSLATE("File name:")); 40457e2f323SJérôme Duval fFileInfoBox->AddChild(fFilename); 405b271ff3fSPhilippe Saint-Pierre fFilename->SetFont(&font, B_FONT_SIZE); 406b271ff3fSPhilippe Saint-Pierre fFilename->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 407b271ff3fSPhilippe Saint-Pierre 408b271ff3fSPhilippe Saint-Pierre r.top += fontHeight; 409b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + fontHeight + 3; 4103ee96407SJérôme Duval fFormat = new BStringView(r, "format", B_TRANSLATE("Format:")); 41157e2f323SJérôme Duval fFileInfoBox->AddChild(fFormat); 412b271ff3fSPhilippe Saint-Pierre fFormat->SetFont(&font, B_FONT_SIZE); 413b271ff3fSPhilippe Saint-Pierre fFormat->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 414b271ff3fSPhilippe Saint-Pierre 415b271ff3fSPhilippe Saint-Pierre r.top += fontHeight; 416b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + fontHeight + 3; 4173ee96407SJérôme Duval fCompression = new BStringView(r, "compression", 4183ee96407SJérôme Duval B_TRANSLATE("Compression:")); 41957e2f323SJérôme Duval fFileInfoBox->AddChild(fCompression); 420b271ff3fSPhilippe Saint-Pierre fCompression->SetFont(&font, B_FONT_SIZE); 421b271ff3fSPhilippe Saint-Pierre fCompression->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 422b271ff3fSPhilippe Saint-Pierre 423b271ff3fSPhilippe Saint-Pierre r.top += fontHeight; 424b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + fontHeight + 3; 4253ee96407SJérôme Duval fChannels = new BStringView(r, "channels", B_TRANSLATE("Channels:")); 42657e2f323SJérôme Duval fFileInfoBox->AddChild(fChannels); 427b271ff3fSPhilippe Saint-Pierre fChannels->SetFont(&font, B_FONT_SIZE); 428b271ff3fSPhilippe Saint-Pierre fChannels->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 429b271ff3fSPhilippe Saint-Pierre 430b271ff3fSPhilippe Saint-Pierre r.top += fontHeight; 431b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + fontHeight + 3; 4323ee96407SJérôme Duval fSampleSize = new BStringView(r, "samplesize", 4333ee96407SJérôme Duval B_TRANSLATE("Sample size:")); 43457e2f323SJérôme Duval fFileInfoBox->AddChild(fSampleSize); 435b271ff3fSPhilippe Saint-Pierre fSampleSize->SetFont(&font, B_FONT_SIZE); 436b271ff3fSPhilippe Saint-Pierre fSampleSize->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 437b271ff3fSPhilippe Saint-Pierre 438b271ff3fSPhilippe Saint-Pierre r.top += fontHeight; 439b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + fontHeight + 3; 4403ee96407SJérôme Duval fSampleRate = new BStringView(r, "samplerate", 4413ee96407SJérôme Duval B_TRANSLATE("Sample rate:")); 44257e2f323SJérôme Duval fFileInfoBox->AddChild(fSampleRate); 443b271ff3fSPhilippe Saint-Pierre fSampleRate->SetFont(&font, B_FONT_SIZE); 444b271ff3fSPhilippe Saint-Pierre fSampleRate->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 445b271ff3fSPhilippe Saint-Pierre 446b271ff3fSPhilippe Saint-Pierre r.top += fontHeight; 447b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + fontHeight + 3; 4483ee96407SJérôme Duval fDuration = new BStringView(r, "duration", B_TRANSLATE("Duration:")); 44957e2f323SJérôme Duval fFileInfoBox->AddChild(fDuration); 450b271ff3fSPhilippe Saint-Pierre fDuration->SetFont(&font, B_FONT_SIZE); 451b271ff3fSPhilippe Saint-Pierre fDuration->SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 452b271ff3fSPhilippe Saint-Pierre 453b271ff3fSPhilippe Saint-Pierre fFileInfoBox->ResizeTo(fFileInfoBox->Frame().Width(), 454b271ff3fSPhilippe Saint-Pierre r.bottom + fontHeight / 2.0f); 455b271ff3fSPhilippe Saint-Pierre fDeployedHeight = MIN_HEIGHT + fFileInfoBox->Bounds().Height() + 40.0f; 45657e2f323SJérôme Duval 45757e2f323SJérôme Duval // Input selection lists all available physical inputs that produce 45857e2f323SJérôme Duval // buffers with B_MEDIA_RAW_AUDIO format data. 4593ee96407SJérôme Duval popup = new BPopUpMenu(B_TRANSLATE("Input")); 460a8ee8b8fSStephan Aßmus const int maxInputCount = 64; 461a8ee8b8fSStephan Aßmus dormant_node_info dni[maxInputCount]; 46257e2f323SJérôme Duval 463a8ee8b8fSStephan Aßmus int32 real_count = maxInputCount; 464*739fd34cSBarrett 46557e2f323SJérôme Duval error = fRoster->GetDormantNodes(dni, &real_count, 0, &output_format, 46657e2f323SJérôme Duval 0, B_BUFFER_PRODUCER | B_PHYSICAL_INPUT); 467a8ee8b8fSStephan Aßmus if (real_count > maxInputCount) { 4687f4d1af4SSergei Reznikov WINDOW((stderr, "dropped %" B_PRId32 " inputs\n", real_count - maxInputCount)); 469a8ee8b8fSStephan Aßmus real_count = maxInputCount; 47057e2f323SJérôme Duval } 4717974d3dcSStephan Aßmus char selected_name[B_MEDIA_NAME_LENGTH] = "Default input"; 47257e2f323SJérôme Duval BMessage * msg; 47357e2f323SJérôme Duval BMenuItem * item; 474ad0c65eaSJérôme Duval for (int i = 0; i < real_count; i++) { 47557e2f323SJérôme Duval msg = new BMessage(INPUT_SELECTED); 476ad0c65eaSJérôme Duval msg->AddData("node", B_RAW_TYPE, &dni[i], sizeof(dni[i])); 477ad0c65eaSJérôme Duval item = new BMenuItem(dni[i].name, msg); 47857e2f323SJérôme Duval popup->AddItem(item); 47957e2f323SJérôme Duval media_node_id ni[12]; 48057e2f323SJérôme Duval int32 ni_count = 12; 481ad0c65eaSJérôme Duval error = fRoster->GetInstancesFor(dni[i].addon, dni[i].flavor_id, 482ad0c65eaSJérôme Duval ni, &ni_count); 483b1ed9a63SJérôme Duval if (error == B_OK) { 484b1ed9a63SJérôme Duval for (int j = 0; j < ni_count; j++) { 485ad0c65eaSJérôme Duval if (ni[j] == fAudioInputNode.node) { 486ad0c65eaSJérôme Duval strcpy(selected_name, dni[i].name); 48757e2f323SJérôme Duval break; 48857e2f323SJérôme Duval } 48957e2f323SJérôme Duval } 490b1ed9a63SJérôme Duval } 491b1ed9a63SJérôme Duval } 49257e2f323SJérôme Duval 49357e2f323SJérôme Duval // Create the actual widget 494b271ff3fSPhilippe Saint-Pierre r = fFileInfoBox->Bounds(); 495b271ff3fSPhilippe Saint-Pierre r.top = r.bottom + 2; 496b271ff3fSPhilippe Saint-Pierre r.bottom = r.top + 18; 49757e2f323SJérôme Duval r.InsetBy(10, 10); 4983ee96407SJérôme Duval fInputField = new BMenuField(r, "Input", B_TRANSLATE("Input:"), popup); 499b1ed9a63SJérôme Duval fInputField->SetDivider(fInputField->StringWidth(B_TRANSLATE("Input:")) 500b1ed9a63SJérôme Duval + 4.0f); 50157e2f323SJérôme Duval fBottomBox->AddChild(fInputField); 50257e2f323SJérôme Duval 50357e2f323SJérôme Duval fBottomBox->AddChild(fFileInfoBox); 50457e2f323SJérôme Duval 50557e2f323SJérôme Duval fBottomBox->Hide(); 506b271ff3fSPhilippe Saint-Pierre CalcSizes(MIN_WIDTH, MIN_HEIGHT); 507b271ff3fSPhilippe Saint-Pierre ResizeTo(Frame().Width(), MIN_HEIGHT); 50857e2f323SJérôme Duval 50957e2f323SJérôme Duval popup->Superitem()->SetLabel(selected_name); 51057e2f323SJérôme Duval 51157e2f323SJérôme Duval // Make sure the save panel is happy. 5125fa77532SJérôme Duval fSavePanel = new BFilePanel(B_SAVE_PANEL); 5135fa77532SJérôme Duval fSavePanel->SetTarget(this); 51457e2f323SJérôme Duval } 51557e2f323SJérôme Duval catch (...) { 51657e2f323SJérôme Duval goto bad_mojo; 51757e2f323SJérôme Duval } 51857e2f323SJérôme Duval UpdateButtons(); 51957e2f323SJérôme Duval return B_OK; 52057e2f323SJérôme Duval 52157e2f323SJérôme Duval // Error handling. 52257e2f323SJérôme Duval bad_mojo: 523b1ed9a63SJérôme Duval if (error >= 0) 52457e2f323SJérôme Duval error = B_ERROR; 525*739fd34cSBarrett if (fRecorder) 526*739fd34cSBarrett delete fRecorder; 52757e2f323SJérôme Duval 52857e2f323SJérôme Duval delete fPlayer; 529b1ed9a63SJérôme Duval if (!fInputField) 53057e2f323SJérôme Duval delete popup; 53157e2f323SJérôme Duval return error; 53257e2f323SJérôme Duval } 53357e2f323SJérôme Duval 53457e2f323SJérôme Duval 53557e2f323SJérôme Duval bool 53657e2f323SJérôme Duval RecorderWindow::QuitRequested() // this means Close pressed 53757e2f323SJérôme Duval { 53857e2f323SJérôme Duval StopRecording(); 53957e2f323SJérôme Duval StopPlaying(); 54057e2f323SJérôme Duval be_app->PostMessage(B_QUIT_REQUESTED); 54157e2f323SJérôme Duval return true; 54257e2f323SJérôme Duval } 54357e2f323SJérôme Duval 54457e2f323SJérôme Duval 54557e2f323SJérôme Duval void 54657e2f323SJérôme Duval RecorderWindow::MessageReceived(BMessage * message) 54757e2f323SJérôme Duval { 54857e2f323SJérôme Duval // Your average generic message dispatching switch() statement. 54957e2f323SJérôme Duval switch (message->what) { 55057e2f323SJérôme Duval case INPUT_SELECTED: 55157e2f323SJérôme Duval Input(message); 55257e2f323SJérôme Duval break; 55357e2f323SJérôme Duval case SOUND_SELECTED: 55457e2f323SJérôme Duval Selected(message); 55557e2f323SJérôme Duval break; 55657e2f323SJérôme Duval case STOP_PLAYING: 55757e2f323SJérôme Duval StopPlaying(); 55857e2f323SJérôme Duval break; 55957e2f323SJérôme Duval case STOP_RECORDING: 56057e2f323SJérôme Duval StopRecording(); 56157e2f323SJérôme Duval break; 56257e2f323SJérôme Duval case PLAY_PERIOD: 56357e2f323SJérôme Duval if (fPlayer) { 56457e2f323SJérôme Duval if (fPlayer->HasData()) 56557e2f323SJérôme Duval fPlayButton->SetPlaying(); 56657e2f323SJérôme Duval else 56757e2f323SJérôme Duval fPlayButton->SetPaused(); 56857e2f323SJérôme Duval } 56957e2f323SJérôme Duval break; 57057e2f323SJérôme Duval case RECORD_PERIOD: 57157e2f323SJérôme Duval fRecordButton->SetRecording(); 57257e2f323SJérôme Duval break; 57357e2f323SJérôme Duval case RECORD: 57457e2f323SJérôme Duval Record(message); 57557e2f323SJérôme Duval break; 57657e2f323SJérôme Duval case STOP: 57757e2f323SJérôme Duval Stop(message); 57857e2f323SJérôme Duval break; 57957e2f323SJérôme Duval case PLAY: 58057e2f323SJérôme Duval Play(message); 58157e2f323SJérôme Duval break; 58257e2f323SJérôme Duval case SAVE: 58357e2f323SJérôme Duval Save(message); 58457e2f323SJérôme Duval break; 58557e2f323SJérôme Duval case B_SAVE_REQUESTED: 58657e2f323SJérôme Duval DoSave(message); 58757e2f323SJérôme Duval break; 58857e2f323SJérôme Duval case VIEW_LIST: 58957e2f323SJérôme Duval if (fUpDownButton->Value() == B_CONTROL_ON) { 59057e2f323SJérôme Duval fBottomBox->Show(); 591b271ff3fSPhilippe Saint-Pierre CalcSizes(MIN_WIDTH, fDeployedHeight); 592b271ff3fSPhilippe Saint-Pierre ResizeTo(Frame().Width(), fDeployedHeight); 59357e2f323SJérôme Duval } else { 59457e2f323SJérôme Duval fBottomBox->Hide(); 595b271ff3fSPhilippe Saint-Pierre CalcSizes(MIN_WIDTH, MIN_HEIGHT); 596b271ff3fSPhilippe Saint-Pierre ResizeTo(Frame().Width(), MIN_HEIGHT); 59757e2f323SJérôme Duval 59857e2f323SJérôme Duval } 59957e2f323SJérôme Duval break; 60057e2f323SJérôme Duval case UPDATE_TRACKSLIDER: 60157e2f323SJérôme Duval { 60257e2f323SJérôme Duval bigtime_t timestamp = fPlayTrack->CurrentTime(); 60357e2f323SJérôme Duval fTrackSlider->SetMainTime(timestamp, false); 60457e2f323SJérôme Duval fScopeView->SetMainTime(timestamp); 60557e2f323SJérôme Duval } 60657e2f323SJérôme Duval break; 60757e2f323SJérôme Duval case POSITION_CHANGED: 60857e2f323SJérôme Duval { 60957e2f323SJérôme Duval bigtime_t right, left, main; 61057e2f323SJérôme Duval if (message->FindInt64("main", &main) == B_OK) { 61157e2f323SJérôme Duval if (fPlayTrack) { 61257e2f323SJérôme Duval fPlayTrack->SeekToTime(fTrackSlider->MainTime()); 61357e2f323SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 61457e2f323SJérôme Duval } 61557e2f323SJérôme Duval fScopeView->SetMainTime(main); 61657e2f323SJérôme Duval } 61757e2f323SJérôme Duval if (message->FindInt64("right", &right) == B_OK) { 618b1ed9a63SJérôme Duval if (fPlayTrack) { 619ad0c65eaSJérôme Duval fPlayLimit = MIN(fPlayFrames, 620b1ed9a63SJérôme Duval (off_t)(right * fPlayFormat.u.raw_audio.frame_rate 621b1ed9a63SJérôme Duval / 1000000LL)); 622b1ed9a63SJérôme Duval } 62357e2f323SJérôme Duval fScopeView->SetRightTime(right); 62457e2f323SJérôme Duval } 625b1ed9a63SJérôme Duval if (message->FindInt64("left", &left) == B_OK) 62657e2f323SJérôme Duval fScopeView->SetLeftTime(left); 62757e2f323SJérôme Duval break; 628b1ed9a63SJérôme Duval } 629948356deSJérôme Duval case LOOP: 630948356deSJérôme Duval fLooping = fLoopButton->ButtonState(); 631948356deSJérôme Duval break; 63257e2f323SJérôme Duval case B_SIMPLE_DATA: 63357e2f323SJérôme Duval case B_REFS_RECEIVED: 63457e2f323SJérôme Duval { 63557e2f323SJérôme Duval RefsReceived(message); 63657e2f323SJérôme Duval break; 63757e2f323SJérôme Duval } 638019ed09bSJérôme Duval case B_COPY_TARGET: 639019ed09bSJérôme Duval CopyTarget(message); 640019ed09bSJérôme Duval break; 64157e2f323SJérôme Duval default: 64257e2f323SJérôme Duval BWindow::MessageReceived(message); 64357e2f323SJérôme Duval break; 64457e2f323SJérôme Duval } 64557e2f323SJérôme Duval } 64657e2f323SJérôme Duval 64757e2f323SJérôme Duval 64857e2f323SJérôme Duval void 64957e2f323SJérôme Duval RecorderWindow::Record(BMessage * message) 65057e2f323SJérôme Duval { 65157e2f323SJérôme Duval // User pressed Record button 65257e2f323SJérôme Duval fRecording = true; 65357e2f323SJérôme Duval if (fButtonState != btnPaused) { 65457e2f323SJérôme Duval StopRecording(); 65557e2f323SJérôme Duval return; // user is too fast on the mouse 65657e2f323SJérôme Duval } 65757e2f323SJérôme Duval SetButtonState(btnRecording); 65857e2f323SJérôme Duval fRecordButton->SetRecording(); 65957e2f323SJérôme Duval 66057e2f323SJérôme Duval char name[256]; 66157e2f323SJérôme Duval // Create a file with a temporary name 66257e2f323SJérôme Duval status_t err = NewTempName(name); 66357e2f323SJérôme Duval if (err < B_OK) { 6643ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot find an unused name to use for the " 6653ee96407SJérôme Duval "new recording"), err); 66657e2f323SJérôme Duval return; 66757e2f323SJérôme Duval } 66857e2f323SJérôme Duval // Find the file so we can refer to it later 66957e2f323SJérôme Duval err = fTempDir.FindEntry(name, &fRecEntry); 67057e2f323SJérôme Duval if (err < B_OK) { 6713ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot find the temporary file created to " 6723ee96407SJérôme Duval "hold the new recording"), err); 67357e2f323SJérôme Duval return; 67457e2f323SJérôme Duval } 67557e2f323SJérôme Duval err = fRecFile.SetTo(&fTempDir, name, O_RDWR); 67657e2f323SJérôme Duval if (err < B_OK) { 6773ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot open the temporary file created to " 6783ee96407SJérôme Duval "hold the new recording"), err); 67957e2f323SJérôme Duval fRecEntry.Unset(); 68057e2f323SJérôme Duval return; 68157e2f323SJérôme Duval } 68257e2f323SJérôme Duval // Reserve space on disk (creates fewer fragments) 68307db0c6fSJérôme Duval err = fRecFile.SetSize(4 * fRecordFormat.u.raw_audio.channel_count 6843ee96407SJérôme Duval * fRecordFormat.u.raw_audio.frame_rate 6853ee96407SJérôme Duval * (fRecordFormat.u.raw_audio.format 686ad0c65eaSJérôme Duval & media_raw_audio_format::B_AUDIO_SIZE_MASK)); 68757e2f323SJérôme Duval if (err < B_OK) { 6883ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot record a sound that long"), err); 68957e2f323SJérôme Duval fRecEntry.Remove(); 69057e2f323SJérôme Duval fRecEntry.Unset(); 69157e2f323SJérôme Duval return; 69257e2f323SJérôme Duval } 69357e2f323SJérôme Duval fRecSize = 0; 69457e2f323SJérôme Duval 69557e2f323SJérôme Duval fRecFile.Seek(sizeof(struct wave_struct), SEEK_SET); 69657e2f323SJérôme Duval 69757e2f323SJérôme Duval // Hook up input 69857e2f323SJérôme Duval err = MakeRecordConnection(fAudioInputNode); 69957e2f323SJérôme Duval if (err < B_OK) { 7003ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot connect to the selected sound input"), 7013ee96407SJérôme Duval err); 70257e2f323SJérôme Duval fRecEntry.Remove(); 70357e2f323SJérôme Duval fRecEntry.Unset(); 70457e2f323SJérôme Duval return; 70557e2f323SJérôme Duval } 70657e2f323SJérôme Duval 707*739fd34cSBarrett fRecorder->Start(); 70857e2f323SJérôme Duval } 70957e2f323SJérôme Duval 7108e32a27eSPhilippe Saint-Pierre 71157e2f323SJérôme Duval void 71257e2f323SJérôme Duval RecorderWindow::Play(BMessage * message) 71357e2f323SJérôme Duval { 71457e2f323SJérôme Duval if (fPlayer) { 71557e2f323SJérôme Duval // User pressed Play button and playing 71657e2f323SJérôme Duval if (fPlayer->HasData()) 71757e2f323SJérôme Duval fPlayButton->SetPaused(); 71857e2f323SJérôme Duval else 71957e2f323SJérôme Duval fPlayButton->SetPlaying(); 72057e2f323SJérôme Duval fPlayer->SetHasData(!fPlayer->HasData()); 72157e2f323SJérôme Duval return; 72257e2f323SJérôme Duval } 72357e2f323SJérôme Duval 72457e2f323SJérôme Duval SetButtonState(btnPlaying); 72557e2f323SJérôme Duval fPlayButton->SetPlaying(); 72657e2f323SJérôme Duval 72757e2f323SJérôme Duval if (!fPlayTrack) { 7283ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot get the file to play"), B_ERROR); 72957e2f323SJérôme Duval return; 73057e2f323SJérôme Duval } 73157e2f323SJérôme Duval 732ad0c65eaSJérôme Duval fPlayLimit = MIN(fPlayFrames, (off_t)(fTrackSlider->RightTime() 733ad0c65eaSJérôme Duval * fPlayFormat.u.raw_audio.frame_rate / 1000000LL)); 73457e2f323SJérôme Duval fPlayTrack->SeekToTime(fTrackSlider->MainTime()); 73557e2f323SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 73657e2f323SJérôme Duval 73757e2f323SJérôme Duval // Create our internal Node which plays sound, and register it. 7383ee96407SJérôme Duval fPlayer = new BSoundPlayer(fAudioMixerNode, &fPlayFormat.u.raw_audio, 7393ee96407SJérôme Duval "Sound Player"); 74057e2f323SJérôme Duval status_t err = fPlayer->InitCheck(); 741b1ed9a63SJérôme Duval if (err < B_OK) 74257e2f323SJérôme Duval return; 74357e2f323SJérôme Duval 74457e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(fPlayer); 74557e2f323SJérôme Duval fPlayer->SetCallbacks(PlayFile, NotifyPlayFile, this); 74657e2f323SJérôme Duval 74757e2f323SJérôme Duval // And get it going... 74857e2f323SJérôme Duval fPlayer->Start(); 74957e2f323SJérôme Duval fPlayer->SetHasData(true); 75057e2f323SJérôme Duval } 75157e2f323SJérôme Duval 7528e32a27eSPhilippe Saint-Pierre 75357e2f323SJérôme Duval void 75457e2f323SJérôme Duval RecorderWindow::Stop(BMessage * message) 75557e2f323SJérôme Duval { 75657e2f323SJérôme Duval // User pressed Stop button. 75757e2f323SJérôme Duval // Stop recorder. 75857e2f323SJérôme Duval StopRecording(); 75957e2f323SJérôme Duval // Stop player. 76057e2f323SJérôme Duval StopPlaying(); 76157e2f323SJérôme Duval } 76257e2f323SJérôme Duval 7638e32a27eSPhilippe Saint-Pierre 76457e2f323SJérôme Duval void 76557e2f323SJérôme Duval RecorderWindow::Save(BMessage * message) 76657e2f323SJérôme Duval { 76757e2f323SJérôme Duval // User pressed Save button. 76857e2f323SJérôme Duval // Find the item to save. 76957e2f323SJérôme Duval int32 index = fSoundList->CurrentSelection(); 77057e2f323SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(index)); 771b1ed9a63SJérôme Duval if ((! pItem) || (pItem->Entry().InitCheck() != B_OK)) 77257e2f323SJérôme Duval return; 77357e2f323SJérôme Duval 77457e2f323SJérôme Duval // Update the save panel and show it. 77557e2f323SJérôme Duval char filename[B_FILE_NAME_LENGTH]; 77657e2f323SJérôme Duval pItem->Entry().GetName(filename); 77757e2f323SJérôme Duval BMessage saveMsg(B_SAVE_REQUESTED); 77857e2f323SJérôme Duval entry_ref ref; 77957e2f323SJérôme Duval pItem->Entry().GetRef(&ref); 78057e2f323SJérôme Duval 781a4797804SJérôme Duval if (saveMsg.AddPointer("sound list item", pItem) != B_OK) 782a4797804SJérôme Duval fprintf(stderr, "failed to add pItem\n"); 7835fa77532SJérôme Duval fSavePanel->SetSaveText(filename); 7845fa77532SJérôme Duval fSavePanel->SetMessage(&saveMsg); 7855fa77532SJérôme Duval fSavePanel->Show(); 78657e2f323SJérôme Duval } 78757e2f323SJérôme Duval 7888e32a27eSPhilippe Saint-Pierre 78957e2f323SJérôme Duval void 79057e2f323SJérôme Duval RecorderWindow::DoSave(BMessage * message) 79157e2f323SJérôme Duval { 79257e2f323SJérôme Duval // User picked a place to put the file. 79357e2f323SJérôme Duval // Find the location of the old (e.g. 79457e2f323SJérôme Duval // temporary file), and the name of the 79557e2f323SJérôme Duval // new file to save. 79657e2f323SJérôme Duval entry_ref old_ref, new_dir_ref; 79757e2f323SJérôme Duval const char* new_name; 79857e2f323SJérôme Duval SoundListItem* pItem; 79957e2f323SJérôme Duval 80057e2f323SJérôme Duval if ((message->FindPointer("sound list item", (void**) &pItem) == B_OK) 80157e2f323SJérôme Duval && (message->FindRef("directory", &new_dir_ref) == B_OK) 802b1ed9a63SJérôme Duval && (message->FindString("name", &new_name) == B_OK)) { 80357e2f323SJérôme Duval BEntry& oldEntry = pItem->Entry(); 80457e2f323SJérôme Duval BFile oldFile(&oldEntry, B_READ_WRITE); 80557e2f323SJérôme Duval if (oldFile.InitCheck() != B_OK) 80657e2f323SJérôme Duval return; 80757e2f323SJérôme Duval 80857e2f323SJérôme Duval BDirectory newDir(&new_dir_ref); 80957e2f323SJérôme Duval if (newDir.InitCheck() != B_OK) 81057e2f323SJérôme Duval return; 81157e2f323SJérôme Duval 81257e2f323SJérôme Duval BFile newFile; 81357e2f323SJérôme Duval newDir.CreateFile(new_name, &newFile); 81457e2f323SJérôme Duval 81557e2f323SJérôme Duval if (newFile.InitCheck() != B_OK) 81657e2f323SJérôme Duval return; 81757e2f323SJérôme Duval 81857e2f323SJérôme Duval status_t err = CopyFile(newFile, oldFile); 81957e2f323SJérôme Duval 82057e2f323SJérôme Duval if (err == B_OK) { 82157e2f323SJérôme Duval // clean up the sound list and item 82257e2f323SJérôme Duval if (pItem->IsTemp()) 82357e2f323SJérôme Duval oldEntry.Remove(); // blows away temp file! 82457e2f323SJérôme Duval oldEntry.SetTo(&newDir, new_name); 82557e2f323SJérôme Duval pItem->SetTemp(false); // don't blow the new entry away when we exit! 82657e2f323SJérôme Duval fSoundList->Invalidate(); 82757e2f323SJérôme Duval } 82857e2f323SJérôme Duval } else { 82957e2f323SJérôme Duval WINDOW((stderr, "Couldn't save file.\n")); 83057e2f323SJérôme Duval } 83157e2f323SJérôme Duval } 83257e2f323SJérôme Duval 83357e2f323SJérôme Duval 83457e2f323SJérôme Duval void 83557e2f323SJérôme Duval RecorderWindow::Input(BMessage * message) 83657e2f323SJérôme Duval { 83757e2f323SJérôme Duval // User selected input from pop-up 83857e2f323SJérôme Duval const dormant_node_info * dni = 0; 83957e2f323SJérôme Duval ssize_t size = 0; 840b1ed9a63SJérôme Duval if (message->FindData("node", B_RAW_TYPE, (const void **)&dni, &size)) 84157e2f323SJérôme Duval return; // bad input selection message 84257e2f323SJérôme Duval 84357e2f323SJérôme Duval media_node_id node_id; 84457e2f323SJérôme Duval status_t error = fRoster->GetInstancesFor(dni->addon, dni->flavor_id, &node_id); 845b1ed9a63SJérôme Duval if (error != B_OK) 84657e2f323SJérôme Duval fRoster->InstantiateDormantNode(*dni, &fAudioInputNode); 847b1ed9a63SJérôme Duval else 84857e2f323SJérôme Duval fRoster->GetNodeFor(node_id, &fAudioInputNode); 84957e2f323SJérôme Duval } 85057e2f323SJérôme Duval 8518e32a27eSPhilippe Saint-Pierre 85257e2f323SJérôme Duval void 85357e2f323SJérôme Duval RecorderWindow::Selected(BMessage * message) 85457e2f323SJérôme Duval { 85557e2f323SJérôme Duval // User selected a sound in list view 856c6f8aa29SJérôme Duval int32 selIdx = fSoundList->CurrentSelection(); 857c6f8aa29SJérôme Duval SoundListItem* pItem = dynamic_cast<SoundListItem*>(fSoundList->ItemAt(selIdx)); 858c6f8aa29SJérôme Duval if (!pItem) 859c6f8aa29SJérôme Duval return; 860c6f8aa29SJérôme Duval status_t err = UpdatePlayFile(pItem, true); 861c6f8aa29SJérôme Duval if (err != B_OK) { 8623ee96407SJérôme Duval ErrorAlert(B_TRANSLATE("Cannot recognize this file as a media file"), 863ad0c65eaSJérôme Duval err == B_MEDIA_NO_HANDLER ? B_OK : err); 864c6f8aa29SJérôme Duval RemoveCurrentSoundItem(); 865c6f8aa29SJérôme Duval } 86657e2f323SJérôme Duval UpdateButtons(); 86757e2f323SJérôme Duval } 86857e2f323SJérôme Duval 8698e32a27eSPhilippe Saint-Pierre 87057e2f323SJérôme Duval status_t 87157e2f323SJérôme Duval RecorderWindow::MakeRecordConnection(const media_node & input) 87257e2f323SJérôme Duval { 87357e2f323SJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection()\n")); 87457e2f323SJérôme Duval 875*739fd34cSBarrett status_t err = B_OK; 876*739fd34cSBarrett media_output audioOutput; 877*739fd34cSBarrett 878*739fd34cSBarrett if (!fRecorder->IsConnected()) { 87957e2f323SJérôme Duval // Find an available output for the given input node. 88057e2f323SJérôme Duval int32 count = 0; 881*739fd34cSBarrett err = fRoster->GetFreeOutputsFor(input, &audioOutput, 1, 882*739fd34cSBarrett &count, B_MEDIA_RAW_AUDIO); 883*739fd34cSBarrett 88457e2f323SJérôme Duval if (err < B_OK) { 885ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 886ad0c65eaSJérôme Duval " couldn't get free outputs from audio input node\n")); 88757e2f323SJérôme Duval return err; 88857e2f323SJérôme Duval } 889*739fd34cSBarrett 89057e2f323SJérôme Duval if (count < 1) { 891ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 892ad0c65eaSJérôme Duval " no free outputs from audio input node\n")); 89357e2f323SJérôme Duval return B_BUSY; 89457e2f323SJérôme Duval } 89557e2f323SJérôme Duval 896*739fd34cSBarrett } else { 897ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 898*739fd34cSBarrett " audio input node already connected\n")); 899*739fd34cSBarrett 90057e2f323SJérôme Duval return B_BUSY; 90157e2f323SJérôme Duval } 90257e2f323SJérôme Duval 90357e2f323SJérôme Duval // Get a format, any format. 904*739fd34cSBarrett fRecordFormat.u.raw_audio = audioOutput.format.u.raw_audio; 90588fef303SJérôme Duval fRecordFormat.type = B_MEDIA_RAW_AUDIO; 90657e2f323SJérôme Duval 90757e2f323SJérôme Duval // Tell the consumer where we want data to go. 908*739fd34cSBarrett err = fRecorder->SetHooks(RecordFile, NotifyRecordFile, this); 909*739fd34cSBarrett 91057e2f323SJérôme Duval if (err < B_OK) { 911ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 912ad0c65eaSJérôme Duval " couldn't set the sound recorder's hook functions\n")); 91357e2f323SJérôme Duval return err; 91457e2f323SJérôme Duval } 91557e2f323SJérôme Duval 916*739fd34cSBarrett if (!fRecorder->IsConnected()) { 917*739fd34cSBarrett 918*739fd34cSBarrett err = fRecorder->Connect(input, &audioOutput, &fRecordFormat); 919*739fd34cSBarrett 92057e2f323SJérôme Duval if (err < B_OK) { 921ad0c65eaSJérôme Duval CONNECT((stderr, "RecorderWindow::MakeRecordConnection():" 922ad0c65eaSJérôme Duval " failed to connect sound recorder to audio input node.\n")); 923*739fd34cSBarrett 924*739fd34cSBarrett fRecorder->SetHooks(NULL, NULL, NULL); 92557e2f323SJérôme Duval return err; 92657e2f323SJérôme Duval } 927*739fd34cSBarrett } 92857e2f323SJérôme Duval 92957e2f323SJérôme Duval return B_OK; 93057e2f323SJérôme Duval } 93157e2f323SJérôme Duval 93257e2f323SJérôme Duval 93357e2f323SJérôme Duval status_t 93457e2f323SJérôme Duval RecorderWindow::BreakRecordConnection() 93557e2f323SJérôme Duval { 936*739fd34cSBarrett status_t err = B_OK; 93757e2f323SJérôme Duval 938*739fd34cSBarrett err = fRecorder->Stop(true); 939*739fd34cSBarrett if (err < B_OK) 94057e2f323SJérôme Duval return err; 941*739fd34cSBarrett 942*739fd34cSBarrett return fRecorder->Disconnect(); 94357e2f323SJérôme Duval } 94457e2f323SJérôme Duval 9458e32a27eSPhilippe Saint-Pierre 94657e2f323SJérôme Duval status_t 94757e2f323SJérôme Duval RecorderWindow::StopRecording() 94857e2f323SJérôme Duval { 94957e2f323SJérôme Duval if (!fRecording) 95057e2f323SJérôme Duval return B_OK; 95157e2f323SJérôme Duval fRecording = false; 952*739fd34cSBarrett 95357e2f323SJérôme Duval BreakRecordConnection(); 954*739fd34cSBarrett 955*739fd34cSBarrett fRecorder->SetHooks(NULL, NULL, NULL); 956*739fd34cSBarrett 95757e2f323SJérôme Duval if (fRecSize > 0) { 95857e2f323SJérôme Duval 95957e2f323SJérôme Duval wave_struct header; 96057e2f323SJérôme Duval header.riff.riff_id = FOURCC('R','I','F','F'); 961a4797804SJérôme Duval header.riff.len = fRecSize + sizeof(header) - 8; 96257e2f323SJérôme Duval header.riff.wave_id = FOURCC('W','A','V','E'); 96357e2f323SJérôme Duval header.format_chunk.fourcc = FOURCC('f','m','t',' '); 96457e2f323SJérôme Duval header.format_chunk.len = sizeof(header.format); 96557e2f323SJérôme Duval header.format.format_tag = 1; 96688fef303SJérôme Duval header.format.channels = fRecordFormat.u.raw_audio.channel_count; 96788fef303SJérôme Duval header.format.samples_per_sec = (uint32)fRecordFormat.u.raw_audio.frame_rate; 96888fef303SJérôme Duval header.format.avg_bytes_per_sec = (uint32)(fRecordFormat.u.raw_audio.frame_rate 96988fef303SJérôme Duval * fRecordFormat.u.raw_audio.channel_count 97088fef303SJérôme Duval * (fRecordFormat.u.raw_audio.format & 0xf)); 97188fef303SJérôme Duval header.format.bits_per_sample = (fRecordFormat.u.raw_audio.format & 0xf) * 8; 97288fef303SJérôme Duval header.format.block_align = (fRecordFormat.u.raw_audio.format & 0xf) 97388fef303SJérôme Duval * fRecordFormat.u.raw_audio.channel_count; 97457e2f323SJérôme Duval header.data_chunk.fourcc = FOURCC('d','a','t','a'); 97557e2f323SJérôme Duval header.data_chunk.len = fRecSize; 97657e2f323SJérôme Duval fRecFile.Seek(0, SEEK_SET); 97757e2f323SJérôme Duval fRecFile.Write(&header, sizeof(header)); 97857e2f323SJérôme Duval 9793ee96407SJérôme Duval fRecFile.SetSize(fRecSize + sizeof(header)); 9803ee96407SJérôme Duval // We reserve space; make sure we cut off any excess at the end. 98157e2f323SJérôme Duval AddSoundItem(fRecEntry, true); 982b1ed9a63SJérôme Duval } else 98357e2f323SJérôme Duval fRecEntry.Remove(); 984b1ed9a63SJérôme Duval 98557e2f323SJérôme Duval // We're done for this time. 98657e2f323SJérôme Duval fRecEntry.Unset(); 98757e2f323SJérôme Duval // Close the file. 98857e2f323SJérôme Duval fRecFile.Unset(); 98957e2f323SJérôme Duval // No more recording going on. 99057e2f323SJérôme Duval fRecSize = 0; 99157e2f323SJérôme Duval SetButtonState(btnPaused); 99257e2f323SJérôme Duval fRecordButton->SetStopped(); 99357e2f323SJérôme Duval 99457e2f323SJérôme Duval return B_OK; 99557e2f323SJérôme Duval } 99657e2f323SJérôme Duval 99757e2f323SJérôme Duval 99857e2f323SJérôme Duval status_t 99957e2f323SJérôme Duval RecorderWindow::StopPlaying() 100057e2f323SJérôme Duval { 100157e2f323SJérôme Duval if (fPlayer) { 100257e2f323SJérôme Duval fPlayer->Stop(); 100357e2f323SJérôme Duval fPlayer->SetCallbacks(0, 0, 0); 100457e2f323SJérôme Duval fVolumeSlider->SetSoundPlayer(NULL); 100557e2f323SJérôme Duval delete fPlayer; 100657e2f323SJérôme Duval fPlayer = NULL; 100757e2f323SJérôme Duval } 100857e2f323SJérôme Duval SetButtonState(btnPaused); 100957e2f323SJérôme Duval fPlayButton->SetStopped(); 101057e2f323SJérôme Duval fTrackSlider->ResetMainTime(); 101157e2f323SJérôme Duval fScopeView->SetMainTime(*fTrackSlider->MainTime()); 101257e2f323SJérôme Duval return B_OK; 101357e2f323SJérôme Duval } 101457e2f323SJérôme Duval 101557e2f323SJérôme Duval 101657e2f323SJérôme Duval void 101757e2f323SJérôme Duval RecorderWindow::SetButtonState(BtnState state) 101857e2f323SJérôme Duval { 101957e2f323SJérôme Duval fButtonState = state; 102057e2f323SJérôme Duval UpdateButtons(); 102157e2f323SJérôme Duval } 102257e2f323SJérôme Duval 102357e2f323SJérôme Duval 102457e2f323SJérôme Duval void 102557e2f323SJérôme Duval RecorderWindow::UpdateButtons() 102657e2f323SJérôme Duval { 102757e2f323SJérôme Duval bool hasSelection = (fSoundList->CurrentSelection() >= 0); 102857e2f323SJérôme Duval fRecordButton->SetEnabled(fButtonState != btnPlaying); 102957e2f323SJérôme Duval fPlayButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 103057e2f323SJérôme Duval fRewindButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 103157e2f323SJérôme Duval fForwardButton->SetEnabled((fButtonState != btnRecording) && hasSelection); 103257e2f323SJérôme Duval fStopButton->SetEnabled(fButtonState != btnPaused); 103357e2f323SJérôme Duval fSaveButton->SetEnabled(hasSelection && (fButtonState != btnRecording)); 103457e2f323SJérôme Duval fInputField->SetEnabled(fButtonState != btnRecording); 103557e2f323SJérôme Duval } 103657e2f323SJérôme Duval 10375fa77532SJérôme Duval #ifndef __HAIKU__ 10383ee96407SJérôme Duval extern "C" status_t DecodedFormat__11BMediaTrackP12media_format( 10393ee96407SJérôme Duval BMediaTrack *self, media_format *inout_format); 10405fa77532SJérôme Duval #endif 104157e2f323SJérôme Duval 10428e32a27eSPhilippe Saint-Pierre 1043c6f8aa29SJérôme Duval status_t 1044c6f8aa29SJérôme Duval RecorderWindow::UpdatePlayFile(SoundListItem* item, bool updateDisplay) 104557e2f323SJérôme Duval { 1046019ed09bSJérôme Duval fScopeView->CancelRendering(); 1047019ed09bSJérôme Duval StopPlaying(); 1048019ed09bSJérôme Duval StopRecording(); 1049019ed09bSJérôme Duval 1050dbbc34b6SJérôme Duval if (fPlayTrack && fPlayFile) { 10517942339dSJérôme Duval fPlayFile->ReleaseTrack(fPlayTrack); 10527942339dSJérôme Duval fPlayTrack = NULL; 1053dbbc34b6SJérôme Duval } 1054dbbc34b6SJérôme Duval if (fPlayFile) { 1055dbbc34b6SJérôme Duval delete fPlayFile; 10567942339dSJérôme Duval fPlayFile = NULL; 1057dbbc34b6SJérôme Duval } 10587942339dSJérôme Duval 105957e2f323SJérôme Duval status_t err; 1060c6f8aa29SJérôme Duval BEntry& entry = item->Entry(); 106157e2f323SJérôme Duval entry_ref ref; 106257e2f323SJérôme Duval entry.GetRef(&ref); 106357e2f323SJérôme Duval fPlayFile = new BMediaFile(&ref); //, B_MEDIA_FILE_UNBUFFERED); 106457e2f323SJérôme Duval if ((err = fPlayFile->InitCheck()) < B_OK) { 106557e2f323SJérôme Duval delete fPlayFile; 1066dbbc34b6SJérôme Duval fPlayFile = NULL; 1067c6f8aa29SJérôme Duval return err; 106857e2f323SJérôme Duval } 106957e2f323SJérôme Duval 107057e2f323SJérôme Duval for (int ix=0; ix < fPlayFile->CountTracks(); ix++) { 107157e2f323SJérôme Duval BMediaTrack * track = fPlayFile->TrackAt(ix); 107257e2f323SJérôme Duval fPlayFormat.type = B_MEDIA_RAW_AUDIO; 10735fa77532SJérôme Duval #ifdef __HAIKU__ 10745fa77532SJérôme Duval if ((track->DecodedFormat(&fPlayFormat) == B_OK) 10755fa77532SJérôme Duval #else 10765fa77532SJérôme Duval if ((DecodedFormat__11BMediaTrackP12media_format(track, &fPlayFormat) == B_OK) 10775fa77532SJérôme Duval #endif 10785fa77532SJérôme Duval && (fPlayFormat.type == B_MEDIA_RAW_AUDIO)) { 107957e2f323SJérôme Duval fPlayTrack = track; 108057e2f323SJérôme Duval break; 108157e2f323SJérôme Duval } 108257e2f323SJérôme Duval if (track) 108357e2f323SJérôme Duval fPlayFile->ReleaseTrack(track); 108457e2f323SJérôme Duval } 108557e2f323SJérôme Duval 108657e2f323SJérôme Duval if (!fPlayTrack) { 108757e2f323SJérôme Duval delete fPlayFile; 1088019ed09bSJérôme Duval fPlayFile = NULL; 1089c6f8aa29SJérôme Duval return B_STREAM_NOT_FOUND; 109057e2f323SJérôme Duval } 109157e2f323SJérôme Duval 1092c6f8aa29SJérôme Duval if (!updateDisplay) 1093c6f8aa29SJérôme Duval return B_OK; 1094c6f8aa29SJérôme Duval 10953ee96407SJérôme Duval BString filename = B_TRANSLATE("File name: "); 109657e2f323SJérôme Duval filename << ref.name; 109757e2f323SJérôme Duval fFilename->SetText(filename.String()); 109857e2f323SJérôme Duval 10993ee96407SJérôme Duval BString format = B_TRANSLATE("Format: "); 110057e2f323SJérôme Duval media_file_format file_format; 110157e2f323SJérôme Duval if (fPlayFile->GetFileFormatInfo(&file_format) == B_OK) 110257e2f323SJérôme Duval format << file_format.short_name; 11033ee96407SJérôme Duval BString compression = B_TRANSLATE("Compression: "); 110457e2f323SJérôme Duval media_codec_info codec_info; 110557e2f323SJérôme Duval if (fPlayTrack->GetCodecInfo(&codec_info) == B_OK) { 110657e2f323SJérôme Duval if (strcmp(codec_info.short_name, "raw")==0) 11073ee96407SJérôme Duval compression << B_TRANSLATE("None"); 110857e2f323SJérôme Duval else 110957e2f323SJérôme Duval compression << codec_info.short_name; 111057e2f323SJérôme Duval } 11113ee96407SJérôme Duval BString channels = B_TRANSLATE("Channels: "); 111257e2f323SJérôme Duval channels << fPlayFormat.u.raw_audio.channel_count; 11133ee96407SJérôme Duval BString samplesize = B_TRANSLATE("Sample size: "); 11143ee96407SJérôme Duval samplesize << 8 * (fPlayFormat.u.raw_audio.format & 0xf) 11153ee96407SJérôme Duval << B_TRANSLATE(" bits"); 11163ee96407SJérôme Duval BString samplerate = B_TRANSLATE("Sample rate: "); 111757e2f323SJérôme Duval samplerate << (int)fPlayFormat.u.raw_audio.frame_rate; 11183ee96407SJérôme Duval BString durationString = B_TRANSLATE("Duration: "); 111957e2f323SJérôme Duval bigtime_t duration = fPlayTrack->Duration(); 11203ee96407SJérôme Duval durationString << (float)(duration / 1000000.0) << B_TRANSLATE(" seconds"); 112157e2f323SJérôme Duval 112257e2f323SJérôme Duval fFormat->SetText(format.String()); 112357e2f323SJérôme Duval fCompression->SetText(compression.String()); 112457e2f323SJérôme Duval fChannels->SetText(channels.String()); 112557e2f323SJérôme Duval fSampleSize->SetText(samplesize.String()); 112657e2f323SJérôme Duval fSampleRate->SetText(samplerate.String()); 112757e2f323SJérôme Duval fDuration->SetText(durationString.String()); 112857e2f323SJérôme Duval 112957e2f323SJérôme Duval fTrackSlider->SetTotalTime(duration, true); 1130019ed09bSJérôme Duval fScopeView->SetTotalTime(duration, true); 113157e2f323SJérôme Duval fScopeView->RenderTrack(fPlayTrack, fPlayFormat); 11327942339dSJérôme Duval 11337942339dSJérôme Duval fPlayFrames = fPlayTrack->CountFrames(); 1134c6f8aa29SJérôme Duval return B_OK; 113557e2f323SJérôme Duval } 113657e2f323SJérôme Duval 113757e2f323SJérôme Duval 113857e2f323SJérôme Duval void 113957e2f323SJérôme Duval RecorderWindow::ErrorAlert(const char * action, status_t err) 114057e2f323SJérôme Duval { 114157e2f323SJérôme Duval char msg[300]; 1142c6f8aa29SJérôme Duval if (err != B_OK) 11437f4d1af4SSergei Reznikov sprintf(msg, "%s: %s. [%" B_PRIx32 "]", action, strerror(err), (int32) err); 1144c6f8aa29SJérôme Duval else 11453ee96407SJérôme Duval sprintf(msg, "%s.", action); 1146aed35104SHumdinger BAlert* alert = new BAlert("", msg, B_TRANSLATE("Stop")); 1147aed35104SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1148aed35104SHumdinger alert->Go(); 114957e2f323SJérôme Duval } 115057e2f323SJérôme Duval 115157e2f323SJérôme Duval 115257e2f323SJérôme Duval status_t 115357e2f323SJérôme Duval RecorderWindow::NewTempName(char * name) 115457e2f323SJérôme Duval { 115557e2f323SJérôme Duval int init_count = fTempCount; 115657e2f323SJérôme Duval again: 115757e2f323SJérôme Duval if (fTempCount-init_count > 25) { 115857e2f323SJérôme Duval return B_ERROR; 115957e2f323SJérôme Duval } 116057e2f323SJérôme Duval else { 116157e2f323SJérôme Duval fTempCount++; 116257e2f323SJérôme Duval if (fTempCount==0) 116357e2f323SJérôme Duval sprintf(name, "Audio Clip"); 116457e2f323SJérôme Duval else 116557e2f323SJérôme Duval sprintf(name, "Audio Clip %d", fTempCount); 116657e2f323SJérôme Duval BPath path; 116757e2f323SJérôme Duval status_t err; 116857e2f323SJérôme Duval BEntry tempEnt; 116957e2f323SJérôme Duval if ((err = fTempDir.GetEntry(&tempEnt)) < B_OK) { 117057e2f323SJérôme Duval return err; 117157e2f323SJérôme Duval } 117257e2f323SJérôme Duval if ((err = tempEnt.GetPath(&path)) < B_OK) { 117357e2f323SJérôme Duval return err; 117457e2f323SJérôme Duval } 117557e2f323SJérôme Duval path.Append(name); 117657e2f323SJérôme Duval int fd; 117757e2f323SJérôme Duval // Use O_EXCL so we know we created the file (sync with other instances) 117857e2f323SJérôme Duval if ((fd = open(path.Path(), O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) { 117957e2f323SJérôme Duval goto again; 118057e2f323SJérôme Duval } 118157e2f323SJérôme Duval close(fd); 118257e2f323SJérôme Duval } 118357e2f323SJérôme Duval return B_OK; 118457e2f323SJérôme Duval } 118557e2f323SJérôme Duval 118657e2f323SJérôme Duval 118757e2f323SJérôme Duval void 118857e2f323SJérôme Duval RecorderWindow::AddSoundItem(const BEntry& entry, bool temp) 118957e2f323SJérôme Duval { 119057e2f323SJérôme Duval // Create list item to display. 119157e2f323SJérôme Duval SoundListItem * listItem = new SoundListItem(entry, temp); 119257e2f323SJérôme Duval fSoundList->AddItem(listItem); 119357e2f323SJérôme Duval fSoundList->Invalidate(); 119457e2f323SJérôme Duval fSoundList->Select(fSoundList->IndexOf(listItem)); 119557e2f323SJérôme Duval } 119657e2f323SJérôme Duval 1197019ed09bSJérôme Duval 1198019ed09bSJérôme Duval void 1199019ed09bSJérôme Duval RecorderWindow::RemoveCurrentSoundItem() { 1200c6f8aa29SJérôme Duval int32 index = fSoundList->CurrentSelection(); 1201c6f8aa29SJérôme Duval BListItem *item = fSoundList->RemoveItem(index); 1202019ed09bSJérôme Duval delete item; 1203c6f8aa29SJérôme Duval if (index >= fSoundList->CountItems()) 1204c6f8aa29SJérôme Duval index = fSoundList->CountItems() - 1; 1205c6f8aa29SJérôme Duval fSoundList->Select(index); 1206019ed09bSJérôme Duval } 1207019ed09bSJérôme Duval 1208019ed09bSJérôme Duval 120957e2f323SJérôme Duval void 1210ef367a8aSStephan Aßmus RecorderWindow::RecordFile(void* cookie, bigtime_t timestamp, 1211*739fd34cSBarrett void* data, size_t size, const media_format &format) 121257e2f323SJérôme Duval { 121357e2f323SJérôme Duval // Callback called from the SoundConsumer when receiving buffers. 121457e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 121557e2f323SJérôme Duval 121657e2f323SJérôme Duval if (window->fRecording) { 121757e2f323SJérôme Duval // Write the data to file (we don't buffer or guard file access 121857e2f323SJérôme Duval // or anything) 121957e2f323SJérôme Duval window->fRecFile.WriteAt(window->fRecSize, data, size); 1220*739fd34cSBarrett window->fVUView->ComputeLevels(data, size, format.u.raw_audio.format); 122157e2f323SJérôme Duval window->fRecSize += size; 122257e2f323SJérôme Duval } 122357e2f323SJérôme Duval } 122457e2f323SJérôme Duval 122557e2f323SJérôme Duval 122657e2f323SJérôme Duval void 122757e2f323SJérôme Duval RecorderWindow::NotifyRecordFile(void * cookie, int32 code, ...) 122857e2f323SJérôme Duval { 122957e2f323SJérôme Duval if ((code == B_WILL_STOP) || (code == B_NODE_DIES)) { 123057e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 123157e2f323SJérôme Duval // Tell the window we've stopped, if it doesn't 123257e2f323SJérôme Duval // already know. 123357e2f323SJérôme Duval window->PostMessage(STOP_RECORDING); 123457e2f323SJérôme Duval } 123557e2f323SJérôme Duval } 123657e2f323SJérôme Duval 123757e2f323SJérôme Duval 123857e2f323SJérôme Duval void 1239ad0c65eaSJérôme Duval RecorderWindow::PlayFile(void * cookie, void * data, size_t size, 1240ad0c65eaSJérôme Duval const media_raw_audio_format & format) 124157e2f323SJérôme Duval { 124257e2f323SJérôme Duval // Callback called from the SoundProducer when producing buffers. 124357e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 124457e2f323SJérôme Duval int32 frame_size = (window->fPlayFormat.u.raw_audio.format & 0xf) * 124557e2f323SJérôme Duval window->fPlayFormat.u.raw_audio.channel_count; 124657e2f323SJérôme Duval 1247948356deSJérôme Duval if ((window->fPlayFrame < window->fPlayLimit) || window->fLooping) { 1248948356deSJérôme Duval if (window->fPlayFrame >= window->fPlayLimit) { 1249948356deSJérôme Duval bigtime_t left = window->fTrackSlider->LeftTime(); 1250948356deSJérôme Duval window->fPlayTrack->SeekToTime(&left); 1251948356deSJérôme Duval window->fPlayFrame = window->fPlayTrack->CurrentFrame(); 1252948356deSJérôme Duval } 125357e2f323SJérôme Duval int64 frames = 0; 125457e2f323SJérôme Duval window->fPlayTrack->ReadFrames(data, &frames); 1255ad0c65eaSJérôme Duval window->fVUView->ComputeLevels(data, size / frame_size, format.format); 125657e2f323SJérôme Duval window->fPlayFrame += size/frame_size; 125757e2f323SJérôme Duval window->PostMessage(UPDATE_TRACKSLIDER); 125857e2f323SJérôme Duval } else { 125957e2f323SJérôme Duval // we're done! 126057e2f323SJérôme Duval window->PostMessage(STOP_PLAYING); 126157e2f323SJérôme Duval } 126257e2f323SJérôme Duval } 126357e2f323SJérôme Duval 12648e32a27eSPhilippe Saint-Pierre 126557e2f323SJérôme Duval void 1266ad0c65eaSJérôme Duval RecorderWindow::NotifyPlayFile(void * cookie, 1267ad0c65eaSJérôme Duval BSoundPlayer::sound_player_notification code, ...) 126857e2f323SJérôme Duval { 126957e2f323SJérôme Duval if ((code == BSoundPlayer::B_STOPPED) || (code == BSoundPlayer::B_SOUND_DONE)) { 127057e2f323SJérôme Duval RecorderWindow * window = (RecorderWindow *)cookie; 127157e2f323SJérôme Duval // tell the window we've stopped, if it doesn't 127257e2f323SJérôme Duval // already know. 127357e2f323SJérôme Duval window->PostMessage(STOP_PLAYING); 127457e2f323SJérôme Duval } 127557e2f323SJérôme Duval } 127657e2f323SJérôme Duval 127757e2f323SJérôme Duval 127857e2f323SJérôme Duval void 127957e2f323SJérôme Duval RecorderWindow::RefsReceived(BMessage *msg) 128057e2f323SJérôme Duval { 128157e2f323SJérôme Duval entry_ref ref; 128257e2f323SJérôme Duval int32 i = 0; 1283c6f8aa29SJérôme Duval int32 countGood = 0; 1284c6f8aa29SJérôme Duval int32 countBad = 0; 128557e2f323SJérôme Duval 128657e2f323SJérôme Duval while (msg->FindRef("refs", i++, &ref) == B_OK) { 128757e2f323SJérôme Duval 128857e2f323SJérôme Duval BEntry entry(&ref, true); 128957e2f323SJérôme Duval BPath path(&entry); 129057e2f323SJérôme Duval BNode node(&entry); 129157e2f323SJérôme Duval 129257e2f323SJérôme Duval if (node.IsFile()) { 1293c6f8aa29SJérôme Duval SoundListItem * listItem = new SoundListItem(entry, false); 1294c6f8aa29SJérôme Duval if (UpdatePlayFile(listItem) == B_OK) { 1295c6f8aa29SJérôme Duval fSoundList->AddItem(listItem); 1296c6f8aa29SJérôme Duval countGood++; 1297c6f8aa29SJérôme Duval continue; 1298c6f8aa29SJérôme Duval } 1299c6f8aa29SJérôme Duval delete listItem; 130057e2f323SJérôme Duval } else if(node.IsDirectory()) { 130157e2f323SJérôme Duval 130257e2f323SJérôme Duval } 1303c6f8aa29SJérôme Duval countBad++; 1304c6f8aa29SJérôme Duval } 1305c6f8aa29SJérôme Duval 1306193a3956SHumdinger if (countBad == 1 && countGood == 0) { 1307aed35104SHumdinger BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"), 1308193a3956SHumdinger B_TRANSLATE("The file doesn't appear to be an audio file."), 1309193a3956SHumdinger B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1310193a3956SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1311193a3956SHumdinger alert->Go(); 1312193a3956SHumdinger } else if (countBad > 0 && countGood == 0) { 1313193a3956SHumdinger BAlert* alert = new BAlert(B_TRANSLATE("Nothing to play"), 1314193a3956SHumdinger B_TRANSLATE("None of the files appear to be audio files."), 1315aed35104SHumdinger B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1316aed35104SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1317aed35104SHumdinger alert->Go(); 1318b1ed9a63SJérôme Duval } else if (countGood > 0) { 1319aed35104SHumdinger if (countBad > 0) { 1320aed35104SHumdinger BAlert* alert = new BAlert(B_TRANSLATE("Invalid audio files"), 1321193a3956SHumdinger B_TRANSLATE("Some of the files don't appear to be audio files."), 13223ee96407SJérôme Duval B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, 1323aed35104SHumdinger B_WARNING_ALERT); 1324aed35104SHumdinger alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1325aed35104SHumdinger alert->Go(); 1326aed35104SHumdinger } 1327c6f8aa29SJérôme Duval fSoundList->Select(fSoundList->CountItems() - 1); 132857e2f323SJérôme Duval } 132957e2f323SJérôme Duval } 1330019ed09bSJérôme Duval 1331019ed09bSJérôme Duval 1332019ed09bSJérôme Duval void 1333019ed09bSJérôme Duval RecorderWindow::CopyTarget(BMessage *msg) 1334019ed09bSJérôme Duval { 1335019ed09bSJérôme Duval const char *type = NULL; 1336019ed09bSJérôme Duval if (msg->FindString("be:types", &type) == B_OK) { 1337019ed09bSJérôme Duval if (!strcasecmp(type, B_FILE_MIME_TYPE)) { 1338019ed09bSJérôme Duval const char *name; 1339019ed09bSJérôme Duval entry_ref dir; 1340019ed09bSJérôme Duval if (msg->FindString("be:filetypes") == B_OK 1341019ed09bSJérôme Duval && msg->FindString("name", &name) == B_OK 1342019ed09bSJérôme Duval && msg->FindRef("directory", &dir) == B_OK) { 1343019ed09bSJérôme Duval BDirectory directory(&dir); 1344019ed09bSJérôme Duval BFile file(&directory, name, O_RDWR | O_TRUNC); 1345019ed09bSJérôme Duval 1346019ed09bSJérôme Duval // seek time 1347019ed09bSJérôme Duval bigtime_t start = fTrackSlider->LeftTime(); 1348019ed09bSJérôme Duval 1349019ed09bSJérôme Duval // write data 13503ee96407SJérôme Duval bigtime_t diffTime = fTrackSlider->RightTime() 13513ee96407SJérôme Duval - fTrackSlider->LeftTime(); 1352ad0c65eaSJérôme Duval int64 framesToWrite = (int64) (diffTime 1353ad0c65eaSJérôme Duval * fPlayFormat.u.raw_audio.frame_rate / 1000000LL); 1354019ed09bSJérôme Duval int32 frameSize = (fPlayFormat.u.raw_audio.format & 0xf) 1355019ed09bSJérôme Duval * fPlayFormat.u.raw_audio.channel_count; 1356019ed09bSJérôme Duval 1357019ed09bSJérôme Duval wave_struct header; 1358019ed09bSJérôme Duval header.riff.riff_id = FOURCC('R','I','F','F'); 13593ee96407SJérôme Duval header.riff.len 13603ee96407SJérôme Duval = (frameSize * framesToWrite) + sizeof(header) - 8; 1361019ed09bSJérôme Duval header.riff.wave_id = FOURCC('W','A','V','E'); 1362019ed09bSJérôme Duval header.format_chunk.fourcc = FOURCC('f','m','t',' '); 1363019ed09bSJérôme Duval header.format_chunk.len = sizeof(header.format); 1364019ed09bSJérôme Duval header.format.format_tag = 1; 1365019ed09bSJérôme Duval header.format.channels = fPlayFormat.u.raw_audio.channel_count; 13663ee96407SJérôme Duval header.format.samples_per_sec 13673ee96407SJérôme Duval = (uint32)fPlayFormat.u.raw_audio.frame_rate; 13683ee96407SJérôme Duval header.format.avg_bytes_per_sec 13693ee96407SJérôme Duval = (uint32)(fPlayFormat.u.raw_audio.frame_rate 1370019ed09bSJérôme Duval * fPlayFormat.u.raw_audio.channel_count 1371019ed09bSJérôme Duval * (fPlayFormat.u.raw_audio.format & 0xf)); 13723ee96407SJérôme Duval header.format.bits_per_sample 13733ee96407SJérôme Duval = (fPlayFormat.u.raw_audio.format & 0xf) * 8; 1374019ed09bSJérôme Duval header.format.block_align = frameSize; 1375019ed09bSJérôme Duval header.data_chunk.fourcc = FOURCC('d','a','t','a'); 1376019ed09bSJérôme Duval header.data_chunk.len = frameSize * framesToWrite; 1377019ed09bSJérôme Duval file.Seek(0, SEEK_SET); 1378019ed09bSJérôme Duval file.Write(&header, sizeof(header)); 1379019ed09bSJérôme Duval 1380019ed09bSJérôme Duval char *data = (char *)malloc(fPlayFormat.u.raw_audio.buffer_size); 1381019ed09bSJérôme Duval 1382c6f8aa29SJérôme Duval fPlayTrack->SeekToTime(&start); 1383c6f8aa29SJérôme Duval fPlayFrame = fPlayTrack->CurrentFrame(); 1384019ed09bSJérôme Duval while (framesToWrite > 0) { 1385019ed09bSJérôme Duval int64 frames = 0; 1386019ed09bSJérôme Duval status_t err = fPlayTrack->ReadFrames(data, &frames); 1387c6f8aa29SJérôme Duval if (frames <= 0 || err != B_OK) { 1388c6f8aa29SJérôme Duval if (err != B_OK) 1389c6f8aa29SJérôme Duval fprintf(stderr, "CopyTarget: ReadFrames failed\n"); 1390019ed09bSJérôme Duval break; 1391c6f8aa29SJérôme Duval } 1392019ed09bSJérôme Duval file.Write(data, frames * frameSize); 1393019ed09bSJérôme Duval framesToWrite -= frames; 1394019ed09bSJérôme Duval } 1395019ed09bSJérôme Duval 1396019ed09bSJérôme Duval file.Sync(); 1397019ed09bSJérôme Duval free(data); 1398019ed09bSJérôme Duval BNodeInfo nodeInfo(&file); 1399019ed09bSJérôme Duval // set type 1400019ed09bSJérôme Duval } 1401019ed09bSJérôme Duval } else { 1402019ed09bSJérôme Duval 1403019ed09bSJérôme Duval } 1404019ed09bSJérôme Duval } 1405019ed09bSJérôme Duval } 1406