Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "PlaybackManager.h"
28
29PlaybackManager::PlaybackManager(NonnullRefPtr<Audio::ClientConnection> connection)
30 : m_connection(connection)
31{
32 m_timer = Core::Timer::construct(100, [&]() {
33 if (!m_loader)
34 return;
35 next_buffer();
36 });
37 m_timer->stop();
38}
39
40PlaybackManager::~PlaybackManager()
41{
42}
43
44void PlaybackManager::set_loader(OwnPtr<Audio::WavLoader>&& loader)
45{
46 stop();
47 m_loader = move(loader);
48 if (m_loader) {
49 m_total_length = m_loader->total_samples() / static_cast<float>(m_loader->sample_rate());
50 m_timer->start();
51 load_next_buffer();
52 } else {
53 m_timer->stop();
54 }
55}
56
57void PlaybackManager::stop()
58{
59 set_paused(true);
60 m_connection->clear_buffer(true);
61 m_buffers.clear();
62 m_last_seek = 0;
63 m_next_buffer = nullptr;
64 m_current_buffer = nullptr;
65 m_next_ptr = 0;
66
67 if (m_loader)
68 m_loader->reset();
69}
70
71void PlaybackManager::play()
72{
73 set_paused(false);
74}
75
76void PlaybackManager::seek(const int position)
77{
78 if (!m_loader)
79 return;
80
81 m_last_seek = position;
82 bool paused_state = m_paused;
83 set_paused(true);
84
85 m_connection->clear_buffer(true);
86 m_next_buffer = nullptr;
87 m_current_buffer = nullptr;
88 m_next_ptr = 0;
89 m_buffers.clear();
90 m_loader->seek(position);
91
92 if (!paused_state)
93 set_paused(false);
94}
95
96void PlaybackManager::pause()
97{
98 set_paused(true);
99}
100
101void PlaybackManager::remove_dead_buffers()
102{
103 int id = m_connection->get_playing_buffer();
104 int current_id = -1;
105 if (m_current_buffer)
106 current_id = m_current_buffer->shbuf_id();
107
108 if (id >= 0 && id != current_id) {
109 while (!m_buffers.is_empty()) {
110 --m_next_ptr;
111 auto buffer = m_buffers.take_first();
112
113 if (buffer->shbuf_id() == id) {
114 m_current_buffer = buffer;
115 break;
116 }
117 }
118 }
119}
120
121void PlaybackManager::load_next_buffer()
122{
123 if (m_buffers.size() < 10) {
124 for (int i = 0; i < 20 && m_loader->loaded_samples() < m_loader->total_samples(); i++) {
125 auto buffer = m_loader->get_more_samples(PLAYBACK_MANAGER_BUFFER_SIZE);
126 if (buffer)
127 m_buffers.append(buffer);
128 }
129 }
130
131 if (m_next_ptr < m_buffers.size()) {
132 m_next_buffer = m_buffers.at(m_next_ptr++);
133 } else {
134 m_next_buffer = nullptr;
135 }
136}
137
138void PlaybackManager::set_paused(bool paused)
139{
140 if (!m_next_buffer && m_loader)
141 load_next_buffer();
142
143 m_paused = paused;
144 m_connection->set_paused(paused);
145}
146
147bool PlaybackManager::toggle_pause()
148{
149 if (m_paused) {
150 play();
151 } else {
152 pause();
153 }
154 return m_paused;
155}
156
157void PlaybackManager::next_buffer()
158{
159 if (on_update)
160 on_update();
161
162 if (m_paused)
163 return;
164
165 remove_dead_buffers();
166 if (!m_next_buffer) {
167 if (!m_connection->get_remaining_samples() && !m_paused) {
168 dbg() << "Exhausted samples :^)";
169 stop();
170 }
171
172 return;
173 }
174
175 bool enqueued = m_connection->try_enqueue(*m_next_buffer);
176 if (!enqueued)
177 return;
178
179 load_next_buffer();
180}