kwave  18.07.70
PlayerToolBar.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  PlayerToolBar.cpp - Toolbar with control logic for playback/record
3  -------------------
4  begin : 2012-04-23
5  copyright : (C) 2012 by Thomas Eschenbacher
6  email : Thomas Eschenbacher <Thomas.Eschenbacher@gmx.de>
7 
8  ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
18 
19 #include "config.h"
20 
21 #include <QtGlobal>
22 #include <QAction>
23 #include <QIcon>
24 
25 #include <KLocalizedString>
26 #include <KMainWindow>
27 
28 #include "libkwave/FileInfo.h"
29 #include "libkwave/Parser.h"
31 #include "libkwave/SignalManager.h"
32 
33 #include "libgui/MenuManager.h"
34 
35 #include "FileContext.h"
36 #include "PlayerToolBar.h"
37 
39 #define SEEK_LENGTH (m_last_visible / 10)
40 
42 #define UPDATE_MENU(__action__,__entry__) \
43  m_menu_manager.setItemEnabled(_(__entry__), \
44  m_action_##__action__->isEnabled())
45 
47 #define CASE_COMMAND(x) } else if (command == _(x)) {
48 
49 //***************************************************************************
50 Kwave::PlayerToolBar::PlayerToolBar(KMainWindow *parent, const QString &name,
51  Kwave::MenuManager &menu_manager)
52  :KToolBar(name, parent, true),
53  m_context(Q_NULLPTR),
54  m_action_prev(),
55  m_action_rewind(Q_NULLPTR),
56  m_action_record(Q_NULLPTR),
57  m_action_play(Q_NULLPTR),
58  m_action_loop(Q_NULLPTR),
59  m_action_pause(Q_NULLPTR),
60  m_action_stop(Q_NULLPTR),
61  m_action_forward(Q_NULLPTR),
62  m_action_next(Q_NULLPTR),
63  m_pause_timer(Q_NULLPTR),
64  m_blink_on(false),
65  m_playback(Q_NULLPTR),
66  m_menu_manager(menu_manager),
67  m_labels(),
68  m_last_tracks(0),
69  m_last_offset(0),
70  m_last_visible(0),
71  m_last_length(0)
72 {
73  m_action_prev = addAction(
74  QIcon::fromTheme(_("kwave_player_start")),
75  i18n("Previous"),
76  this, SLOT(toolbarRewindPrev()));
77 
78  m_action_rewind = addAction(
79  QIcon::fromTheme(_("kwave_player_rew")),
80  i18n("Rewind"),
81  this, SLOT(toolbarRewind()));
82 
83  m_action_record = addAction(
84  QIcon::fromTheme(_("kwave_player_record")),
85  i18n("Record"),
86  this, SLOT(toolbarRecord()));
87 
88  m_action_play = addAction(
89  QIcon::fromTheme(_("kwave_player_play")),
90  i18n("Start playback"),
91  this, SLOT(toolbarStart()));
92 
93  m_action_loop = addAction(
94  QIcon::fromTheme(_("kwave_player_loop")),
95  i18n("Start playback and loop"),
96  this, SLOT(toolbarLoop()));
97 
98  m_action_pause = addAction(
99  QIcon::fromTheme(_("kwave_player_pause")),
100  QString(),
101  this, SLOT(toolbarPause()));
102 
103  m_action_stop = addAction(
104  QIcon::fromTheme(_("kwave_player_stop")),
105  i18n("Stop playback or loop"),
106  this, SLOT(toolbarStop()));
107 
108  m_action_forward = addAction(
109  QIcon::fromTheme(_("kwave_player_fwd")),
110  i18n("Forward"),
111  this, SLOT(toolbarForward()));
112 
113  m_action_next = addAction(
114  QIcon::fromTheme(_("kwave_player_end")),
115  i18n("Next"),
116  this, SLOT(toolbarForwardNext()));
117 
118  // initial state update
119  updateState();
120 }
121 
122 //***************************************************************************
124 {
125  if (m_pause_timer) delete m_pause_timer;
126  m_pause_timer = Q_NULLPTR;
127  m_context = Q_NULLPTR;
128 }
129 
130 //***************************************************************************
132 {
133  if (context == m_context) return; // nothing to do
134 
135  // disconnect the playback controller of the previous context
136  if (m_context && m_playback) {
137  disconnect(m_playback, SIGNAL(sigPlaybackStarted()),
138  this, SLOT(updateState()));
139  disconnect(m_playback, SIGNAL(sigPlaybackPaused()),
140  this, SLOT(playbackPaused()));
141  disconnect(m_playback, SIGNAL(sigPlaybackStopped()),
142  this, SLOT(updateState()));
143  disconnect(m_playback, SIGNAL(sigPlaybackPos(sample_index_t)),
144  this, SLOT(updatePlaybackPos(sample_index_t)));
145  disconnect(m_context, SIGNAL(sigVisibleRangeChanged(sample_index_t,
149  }
150 
151  // use the new context
152  m_context = context;
153 
154  Kwave::SignalManager *signal =
155  (m_context) ? m_context->signalManager() : Q_NULLPTR;
156  m_playback = (signal) ? &signal->playbackController() : Q_NULLPTR;
157 
158  // connect the playback controller of the new context
159  if (m_context && m_playback) {
160  connect(m_playback, SIGNAL(sigPlaybackStarted()),
161  this, SLOT(updateState()));
162  connect(m_playback, SIGNAL(sigPlaybackPaused()),
163  this, SLOT(playbackPaused()));
164  connect(m_playback, SIGNAL(sigPlaybackStopped()),
165  this, SLOT(updateState()));
166  connect(m_playback, SIGNAL(sigPlaybackPos(sample_index_t)),
167  this, SLOT(updatePlaybackPos(sample_index_t)));
168  connect(m_context, SIGNAL(sigVisibleRangeChanged(sample_index_t,
172  }
173 
174  updateState();
175 }
176 
177 //***************************************************************************
179 {
180  if (context != m_context) return; // not of interest
181  contextSwitched(Q_NULLPTR);
182 }
183 
184 //***************************************************************************
186 {
187  if (!m_action_prev || !m_action_prev->isEnabled() || !m_playback) return;
188 
189  const bool play = m_playback->running() || m_playback->paused();
190  if (play) {
191  const sample_index_t first = m_playback->startPos();
192  sample_index_t prev = m_labels.nextLabelLeft(m_playback->currentPos());
193 
194  if (!m_labels.isEmpty()) {
195  // go to the start marker of the current block
196  if (prev > first) {
197  // seek back to the start of the previous block
198  prev = m_labels.nextLabelLeft(prev);
199  } else if (m_playback->loop()) {
200  // in loop mode: wrap around to last block
201  prev = m_labels.nextLabelLeft(m_playback->endPos());
202  }
203  }
204 
205  if (prev < first) prev = first;
206  m_playback->seekTo(prev);
207  } else {
208  emit sigCommand(_("view:scroll_prev_label()"));
209  }
210 }
211 
212 //***************************************************************************
214 {
215  if (!m_action_rewind || !m_action_rewind->isEnabled() || !m_playback) return;
216 
217  const bool play = m_playback->running() || m_playback->paused();
218  if (play) {
219  sample_index_t first = m_playback->startPos();
220  sample_index_t last = m_playback->endPos();
221  sample_index_t pos = m_playback->currentPos();
222 
223  if (pos < first) pos = first;
224  if (pos > last) pos = last;
225 
226  if ((first + SEEK_LENGTH) <= pos) {
227  // simple case: seek without wraparound
228  pos = pos - SEEK_LENGTH;
229  } else if (m_playback->loop() && ((first + SEEK_LENGTH) <= last)) {
230  // loop mode: wrap around
231  pos = last - (first + SEEK_LENGTH - pos);
232  } else {
233  // normal mode: stick to left border
234  pos = first;
235  }
236 
237  m_playback->seekTo(pos);
238  } else {
239  emit sigCommand(_("view:scroll_left()"));
240  }
241 }
242 
243 //***************************************************************************
245 {
246  if (!m_action_record || !m_action_record->isEnabled()) return;
247 
248  emit sigCommand(_("plugin(record)"));
249 }
250 
251 //***************************************************************************
253 {
254  if (m_playback) m_playback->playbackStart();
255 }
256 
257 //***************************************************************************
259 {
260  if (m_playback) m_playback->playbackLoop();
261 }
262 
263 //***************************************************************************
265 {
266  updateState();
267 
268  if (!m_pause_timer) {
269  m_pause_timer = new QTimer(this);
270  Q_ASSERT(m_pause_timer);
271  if (!m_pause_timer) return;
272 
273  m_blink_on = true;
274  m_pause_timer->start(500);
275  connect(m_pause_timer, SIGNAL(timeout()),
276  this, SLOT(blinkPause()));
277  blinkPause();
278  }
279 }
280 
281 //***************************************************************************
283 {
284  if (!m_action_pause || !m_action_pause->isEnabled() || !m_playback) return;
285 
286  const bool have_signal = (m_last_length && m_last_tracks);
287  const bool playing = m_playback->running();
288 
289  if (!have_signal) return;
290 
291  if (playing) {
292  m_playback->playbackPause();
293  } else {
294  m_playback->playbackContinue();
295  }
296 }
297 
298 //***************************************************************************
300 {
301  if (m_playback) m_playback->playbackStop();
302 }
303 
304 //***************************************************************************
306 {
307  const bool paused = m_playback && m_playback->paused();
308 
309  Q_ASSERT(m_action_pause);
310  if (!m_action_pause) return;
311 
312  m_action_pause->setIcon(QIcon::fromTheme(_((paused && m_blink_on) ?
313  "kwave_player_pause_2" : "kwave_player_pause")));
314 
316 }
317 
318 //***************************************************************************
320 {
321  if (!m_action_forward || !m_action_forward->isEnabled() ||
322  !m_playback) return;
323 
324  const bool play = m_playback->running() || m_playback->paused();
325  if (play) {
326  sample_index_t first = m_playback->startPos();
327  sample_index_t last = m_playback->endPos();
328  sample_index_t pos = m_playback->currentPos();
329 
330  if (pos < first) pos = first;
331  if (pos > last) pos = last;
332 
333  if ((pos + SEEK_LENGTH) <= last) {
334  // simple case: seek without wraparound
335  pos = pos + SEEK_LENGTH;
336  } else if (m_playback->loop() && ((first + SEEK_LENGTH) <= last)) {
337  // loop mode: wrap around
338  pos = first + SEEK_LENGTH - (last - pos);
339  } else {
340  // must be loop mode but selection too small: wrap to left border
341  pos = first;
342  }
343 
344  m_playback->seekTo(pos);
345  } else {
346  emit sigCommand(_("view:scroll_right()"));
347  }
348 }
349 
350 //***************************************************************************
352 {
353  if (!m_action_next || !m_action_next->isEnabled() || !m_playback) return;
354 
355  const bool play = m_playback->running() || m_playback->paused();
356  if (play) {
357  sample_index_t last = m_playback->endPos();
358  sample_index_t next = m_labels.nextLabelRight(m_playback->currentPos());
359  if (next >= last)
360  next = m_playback->startPos(); // wrap around to start
361  m_playback->seekTo(next);
362  } else {
363  emit sigCommand(_("view:scroll_next_label()"));
364  }
365 }
366 
367 //***************************************************************************
369 {
370  if (!m_playback) return;
371 
372  bool have_signal = (m_last_length && m_last_tracks);
373  bool playing = m_playback->running();
374  bool paused = m_playback->paused();
375  bool at_start = (m_last_offset == 0);
376  bool at_end = ((m_last_offset + m_last_visible) >= m_last_length);
377 
378  /* --- seek buttons, depending on mode --- */
379  if (playing || paused) {
380  // seek buttons in playback mode
381  bool loop = m_playback->loop();
382  sample_index_t pos = m_playback->currentPos();
383  sample_index_t first = m_playback->startPos();
384  sample_index_t last = m_playback->endPos();
387  sample_index_t seek_len = SEEK_LENGTH;
388  bool have_label = (prev > first) || (next < last);
389 
390  m_action_prev->setEnabled( (pos > first));
391  m_action_rewind->setEnabled( (pos > first) || (pos > prev));
392  m_action_forward->setEnabled(
393  loop || // wrap around in loop mode
394  ((pos + seek_len) < last) // forward a bit, by range
395  );
396  m_action_next->setEnabled(
397  (loop && have_label) || // wrap around in loop mode
398  (next < last) // snap to next label
399  );
400 
401  UPDATE_MENU(prev, "ID_PLAYBACK_PREV");
402  UPDATE_MENU(rewind, "ID_PLAYBACK_REWIND");
403  UPDATE_MENU(forward,"ID_PLAYBACK_FORWARD");
404  UPDATE_MENU(next, "ID_PLAYBACK_NEXT");
405 
406  // scroll controls per menu
407  m_menu_manager.setItemEnabled(_("ID_SCROLL_START"), false);
408  m_menu_manager.setItemEnabled(_("ID_SCROLL_END"), false);
409  m_menu_manager.setItemEnabled(_("ID_SCROLL_PREV"), false);
410  m_menu_manager.setItemEnabled(_("ID_SCROLL_NEXT"), false);
411  m_menu_manager.setItemEnabled(_("ID_SCROLL_RIGHT"), false);
412  m_menu_manager.setItemEnabled(_("ID_SCROLL_LEFT"), false);
413  } else {
414  // seek buttons in normal mode
415  m_action_prev->setEnabled( have_signal && !at_start);
416  m_action_rewind->setEnabled( have_signal && !at_start);
417  m_action_forward->setEnabled( have_signal && !at_end);
418  m_action_next->setEnabled( have_signal && !at_end);
419  m_menu_manager.setItemEnabled(_("ID_PLAYBACK_PREV"), false);
420  m_menu_manager.setItemEnabled(_("ID_PLAYBACK_REWIND"), false);
421  m_menu_manager.setItemEnabled(_("ID_PLAYBACK_FORWARD"), false);
422  m_menu_manager.setItemEnabled(_("ID_PLAYBACK_NEXT"), false);
423 
424  // scroll controls per menu
425  m_menu_manager.setItemEnabled(_("ID_SCROLL_START"), !at_start);
426  m_menu_manager.setItemEnabled(_("ID_SCROLL_END"), !at_end);
427  m_menu_manager.setItemEnabled(_("ID_SCROLL_PREV"), !at_start);
428  m_menu_manager.setItemEnabled(_("ID_SCROLL_NEXT"), !at_end);
429  m_menu_manager.setItemEnabled(_("ID_SCROLL_RIGHT"), !at_end);
430  m_menu_manager.setItemEnabled(_("ID_SCROLL_LEFT"), !at_start);
431  }
432 
433  /* --- standard record/playback controls --- */
434  m_action_record->setEnabled( !playing && !paused);
435  m_action_play->setEnabled( have_signal && !playing);
436  m_action_loop->setEnabled( have_signal && !playing);
437  m_action_pause->setEnabled( playing || paused);
438  m_action_stop->setEnabled( playing || paused);
439  UPDATE_MENU(record, "ID_RECORD");
440  UPDATE_MENU(play, "ID_PLAYBACK_START");
441  UPDATE_MENU(loop, "ID_PLAYBACK_LOOP");
442  UPDATE_MENU(stop, "ID_PLAYBACK_STOP");
443  m_menu_manager.setItemEnabled(_("ID_PLAYBACK_PAUSE"), playing);
444  m_menu_manager.setItemEnabled(_("ID_PLAYBACK_CONTINUE"), paused);
445 
446  /* handling of blink timer */
447  if (m_pause_timer && !paused) {
448  // stop blinking
449  m_pause_timer->stop();
450  delete m_pause_timer;
451  m_pause_timer = Q_NULLPTR;
452 
453  m_blink_on = false;
454  blinkPause();
455  m_blink_on = false;
456  }
457 
458  /* pause button: continue/pause playback */
459  if (paused) {
460  m_action_pause->setToolTip(i18n("Continue playback"));
461  } else {
462  m_action_pause->setToolTip(i18n("Pause playback"));
463  }
464 
465 }
466 
467 //***************************************************************************
469 {
470  Q_UNUSED(pos);
471  updateState();
472 }
473 
474 //***************************************************************************
475 int Kwave::PlayerToolBar::executeCommand(const QString &command)
476 {
477  int result = 0;
478  Kwave::Parser parser(command);
479 
480  if (false) {
481  CASE_COMMAND("prev")
482  m_action_prev->activate(QAction::Trigger);
483  CASE_COMMAND("rewind")
484  m_action_rewind->activate(QAction::Trigger);
485  CASE_COMMAND("start")
486  m_action_play->activate(QAction::Trigger);
487  CASE_COMMAND("loop")
488  m_action_loop->activate(QAction::Trigger);
489  CASE_COMMAND("pause")
490  m_action_pause->activate(QAction::Trigger);
491  CASE_COMMAND("continue")
492  m_action_pause->activate(QAction::Trigger);
493  CASE_COMMAND("stop")
494  m_action_stop->activate(QAction::Trigger);
495  CASE_COMMAND("forward")
496  m_action_forward->activate(QAction::Trigger);
497  CASE_COMMAND("next")
498  m_action_next->activate(QAction::Trigger);
499  } else {
500  result = -1; // unknown command ?
501  }
502 
503  return result;
504 }
505 
506 //***************************************************************************
508 {
509  const Kwave::FileInfo info(meta_data);
510  sample_index_t length = info.length();
511  unsigned int tracks = info.tracks();
512  bool playing = m_playback && m_playback->running();
513 
514  m_labels = LabelList(meta_data);
515 
516  if (!playing && (length == m_last_length) && (tracks == m_last_tracks))
517  return; // nothing interesting for us changed
518 
519  // transition empty <-> not empty
520  m_last_length = length;
521  m_last_tracks = tracks;
522 
523  updateState();
524 }
525 
526 //***************************************************************************
528  sample_index_t visible,
529  sample_index_t total)
530 {
531  bool changed = (offset != m_last_offset) ||
532  (visible != m_last_visible) ||
533  (total != m_last_length);
534  if (!changed) return;
535 
536  m_last_offset = offset;
537  m_last_visible = visible;
538  m_last_length = total;
539 
540  updateState();
541 }
542 
543 //***************************************************************************
544 //***************************************************************************
void contextSwitched(Kwave::FileContext *context)
void updatePlaybackPos(sample_index_t pos)
void contextDestroyed(Kwave::FileContext *context)
void setItemEnabled(const QString &uid, bool enable)
sample_index_t nextLabelRight(sample_index_t from)
Definition: LabelList.cpp:96
quint64 sample_index_t
Definition: Sample.h:28
Kwave::PlaybackController & playbackController()
bool connect(Kwave::StreamObject &source, const char *output, Kwave::StreamObject &sink, const char *input)
Definition: Connect.cpp:48
sample_index_t nextLabelLeft(sample_index_t from)
Definition: LabelList.cpp:80
const char name[16]
Definition: memcpy.c:510
Manager class for access to Kwave&#39;s menu subsystem.
Definition: MenuManager.h:46
#define UPDATE_MENU(__action__, __entry__)
Kwave::FileContext * m_context
sample_index_t length() const
Definition: FileInfo.cpp:400
#define SEEK_LENGTH
PlayerToolBar(KMainWindow *parent, const QString &name, Kwave::MenuManager &menu_manager)
void visibleRangeChanged(sample_index_t offset, sample_index_t visible, sample_index_t total)
unsigned int tracks() const
Definition: FileInfo.cpp:445
sample_index_t m_last_visible
Kwave::LabelList m_labels
int executeCommand(const QString &command)
#define _(m)
Definition: memcpy.c:66
QPointer< Kwave::PlaybackController > m_playback
#define CASE_COMMAND(x)
sample_index_t m_last_offset
Kwave::SignalManager * signalManager() const
unsigned int m_last_tracks
sample_index_t m_last_length
void sigCommand(const QString &command)
void metaDataChanged(Kwave::MetaDataList meta_data)
QAction * m_action_forward
Kwave::MenuManager & m_menu_manager