Chris@87
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@87
|
2
|
Chris@87
|
3 /*
|
Chris@87
|
4 Sonic Visualiser
|
Chris@87
|
5 An audio file viewer and annotation editor.
|
Chris@87
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@87
|
7 This file copyright 2006 Chris Cannam.
|
Chris@87
|
8
|
Chris@87
|
9 This program is free software; you can redistribute it and/or
|
Chris@87
|
10 modify it under the terms of the GNU General Public License as
|
Chris@87
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@87
|
12 License, or (at your option) any later version. See the file
|
Chris@87
|
13 COPYING included with this distribution for more information.
|
Chris@87
|
14 */
|
Chris@87
|
15
|
Chris@87
|
16 #include "MatrixFileCache.h"
|
Chris@87
|
17 #include "base/TempDirectory.h"
|
Chris@87
|
18
|
Chris@87
|
19 #include <sys/types.h>
|
Chris@87
|
20 #include <sys/stat.h>
|
Chris@87
|
21 #include <fcntl.h>
|
Chris@87
|
22 #include <unistd.h>
|
Chris@87
|
23
|
Chris@87
|
24 #include <iostream>
|
Chris@87
|
25
|
Chris@87
|
26 #include <cstdio>
|
Chris@87
|
27
|
Chris@87
|
28 #include <QFileInfo>
|
Chris@87
|
29 #include <QDir>
|
Chris@87
|
30
|
Chris@87
|
31 MatrixFileCache::MatrixFileCache(QString fileBase, Mode mode) :
|
Chris@87
|
32 m_fd(-1),
|
Chris@87
|
33 m_off(-1),
|
Chris@87
|
34 m_mode(mode),
|
Chris@87
|
35 m_width(0),
|
Chris@87
|
36 m_height(0),
|
Chris@87
|
37 m_rx(0),
|
Chris@87
|
38 m_rw(0),
|
Chris@87
|
39 m_range(0),
|
Chris@87
|
40 m_headerSize(2 * sizeof(size_t))
|
Chris@87
|
41 {
|
Chris@87
|
42 QDir tempDir(TempDirectory::instance()->getPath());
|
Chris@87
|
43 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase)));
|
Chris@87
|
44 bool newFile = !QFileInfo(fileName).exists();
|
Chris@87
|
45
|
Chris@87
|
46 if (newFile && mode == ReadOnly) {
|
Chris@87
|
47 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: Read-only mode "
|
Chris@87
|
48 << "specified, but cache file does not exist" << std::endl;
|
Chris@87
|
49 return;
|
Chris@87
|
50 }
|
Chris@87
|
51
|
Chris@87
|
52 int flags = 0;
|
Chris@87
|
53 mode_t fmode = S_IRUSR | S_IWUSR;
|
Chris@87
|
54
|
Chris@87
|
55 if (mode == ReadWrite) {
|
Chris@87
|
56 flags = O_RDWR | O_CREAT;
|
Chris@87
|
57 } else {
|
Chris@87
|
58 flags = O_RDONLY;
|
Chris@87
|
59 }
|
Chris@87
|
60
|
Chris@87
|
61 if ((m_fd = ::open(fileName.toLocal8Bit(), flags, mode)) < 0) {
|
Chris@87
|
62 ::perror("Open failed");
|
Chris@87
|
63 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: "
|
Chris@87
|
64 << "Failed to open cache file \""
|
Chris@87
|
65 << fileName.toStdString() << "\"";
|
Chris@87
|
66 if (mode == ReadWrite) std::cerr << " for writing";
|
Chris@87
|
67 std::cerr << std::endl;
|
Chris@87
|
68 }
|
Chris@87
|
69
|
Chris@87
|
70 if (newFile) {
|
Chris@87
|
71 resize(0, 0); // write header
|
Chris@87
|
72 } else {
|
Chris@87
|
73 size_t header[2];
|
Chris@87
|
74 if (::read(m_fd, header, 2 * sizeof(size_t))) {
|
Chris@87
|
75 perror("Read failed");
|
Chris@87
|
76 std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: "
|
Chris@87
|
77 << "Failed to read header" << std::endl;
|
Chris@87
|
78 return;
|
Chris@87
|
79 }
|
Chris@87
|
80 m_width = header[0];
|
Chris@87
|
81 m_height = header[1];
|
Chris@87
|
82 seekTo(0, 0);
|
Chris@87
|
83 }
|
Chris@87
|
84
|
Chris@87
|
85 std::cerr << "MatrixFileCache::MatrixFileCache: Done, size is " << m_width << "x" << m_height << std::endl;
|
Chris@87
|
86
|
Chris@87
|
87 }
|
Chris@87
|
88
|
Chris@87
|
89 MatrixFileCache::~MatrixFileCache()
|
Chris@87
|
90 {
|
Chris@87
|
91 if (m_fd >= 0) {
|
Chris@87
|
92 if (::close(m_fd) < 0) {
|
Chris@87
|
93 ::perror("MatrixFileCache::~MatrixFileCache: close failed");
|
Chris@87
|
94 }
|
Chris@87
|
95 }
|
Chris@87
|
96 }
|
Chris@87
|
97
|
Chris@87
|
98 size_t
|
Chris@87
|
99 MatrixFileCache::getWidth() const
|
Chris@87
|
100 {
|
Chris@87
|
101 return m_width;
|
Chris@87
|
102 }
|
Chris@87
|
103
|
Chris@87
|
104 size_t
|
Chris@87
|
105 MatrixFileCache::getHeight() const
|
Chris@87
|
106 {
|
Chris@87
|
107 return m_height;
|
Chris@87
|
108 }
|
Chris@87
|
109
|
Chris@87
|
110 void
|
Chris@87
|
111 MatrixFileCache::resize(size_t w, size_t h)
|
Chris@87
|
112 {
|
Chris@87
|
113 if (m_mode != ReadWrite) {
|
Chris@87
|
114 std::cerr << "ERROR: MatrixFileCache::resize called on read-only cache"
|
Chris@87
|
115 << std::endl;
|
Chris@87
|
116 return;
|
Chris@87
|
117 }
|
Chris@87
|
118
|
Chris@87
|
119 off_t off = m_headerSize + (w * h * sizeof(float));
|
Chris@87
|
120
|
Chris@87
|
121 if (w * h > m_width * m_height) {
|
Chris@87
|
122
|
Chris@87
|
123 if (::lseek(m_fd, off - sizeof(float), SEEK_SET) == (off_t)-1) {
|
Chris@87
|
124 ::perror("Seek failed");
|
Chris@87
|
125 std::cerr << "ERROR: MatrixFileCache::resize(" << w << ", "
|
Chris@87
|
126 << h << "): seek failed, cannot resize" << std::endl;
|
Chris@87
|
127 return;
|
Chris@87
|
128 }
|
Chris@87
|
129
|
Chris@87
|
130 // guess this requires efficient support for sparse files
|
Chris@87
|
131
|
Chris@87
|
132 float f(0);
|
Chris@87
|
133 if (::write(m_fd, &f, sizeof(float)) != sizeof(float)) {
|
Chris@87
|
134 ::perror("WARNING: MatrixFileCache::resize: write failed");
|
Chris@87
|
135 }
|
Chris@87
|
136
|
Chris@87
|
137 } else {
|
Chris@87
|
138
|
Chris@87
|
139 if (::ftruncate(m_fd, off) < 0) {
|
Chris@87
|
140 ::perror("MatrixFileCache::resize: ftruncate failed");
|
Chris@87
|
141 }
|
Chris@87
|
142 }
|
Chris@87
|
143
|
Chris@87
|
144 m_width = 0;
|
Chris@87
|
145 m_height = 0;
|
Chris@87
|
146 m_off = 0;
|
Chris@87
|
147
|
Chris@87
|
148 if (::lseek(m_fd, 0, SEEK_SET) == (off_t)-1) {
|
Chris@87
|
149 ::perror("ERROR: MatrixFileCache::resize: Seek to write header failed");
|
Chris@87
|
150 return;
|
Chris@87
|
151 }
|
Chris@87
|
152
|
Chris@87
|
153 size_t header[2];
|
Chris@87
|
154 header[0] = w;
|
Chris@87
|
155 header[1] = h;
|
Chris@87
|
156 if (::write(m_fd, header, 2 * sizeof(size_t)) != 2 * sizeof(size_t)) {
|
Chris@87
|
157 ::perror("ERROR: MatrixFileCache::resize: Failed to write header");
|
Chris@87
|
158 return;
|
Chris@87
|
159 }
|
Chris@87
|
160
|
Chris@87
|
161 m_width = w;
|
Chris@87
|
162 m_height = h;
|
Chris@87
|
163
|
Chris@87
|
164 seekTo(0, 0);
|
Chris@87
|
165 }
|
Chris@87
|
166
|
Chris@87
|
167 void
|
Chris@87
|
168 MatrixFileCache::reset()
|
Chris@87
|
169 {
|
Chris@87
|
170 if (m_mode != ReadWrite) {
|
Chris@87
|
171 std::cerr << "ERROR: MatrixFileCache::reset called on read-only cache"
|
Chris@87
|
172 << std::endl;
|
Chris@87
|
173 return;
|
Chris@87
|
174 }
|
Chris@87
|
175
|
Chris@87
|
176 //...
|
Chris@87
|
177 }
|
Chris@87
|
178
|
Chris@87
|
179 void
|
Chris@87
|
180 MatrixFileCache::setRangeOfInterest(size_t x, size_t width)
|
Chris@87
|
181 {
|
Chris@87
|
182 }
|
Chris@87
|
183
|
Chris@87
|
184 float
|
Chris@87
|
185 MatrixFileCache::getValueAt(size_t x, size_t y) const
|
Chris@87
|
186 {
|
Chris@87
|
187 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) {
|
Chris@87
|
188 return m_range[x - m_rx][y];
|
Chris@87
|
189 }
|
Chris@87
|
190
|
Chris@87
|
191 if (!seekTo(x, y)) return 0.f;
|
Chris@87
|
192 float value;
|
Chris@87
|
193 if (::read(m_fd, &value, sizeof(float)) != sizeof(float)) {
|
Chris@87
|
194 ::perror("MatrixFileCache::getValueAt: read failed");
|
Chris@87
|
195 return 0.f;
|
Chris@87
|
196 }
|
Chris@87
|
197 return value;
|
Chris@87
|
198 }
|
Chris@87
|
199
|
Chris@87
|
200 void
|
Chris@87
|
201 MatrixFileCache::getColumnAt(size_t x, float *values) const
|
Chris@87
|
202 {
|
Chris@87
|
203 if (m_rw > 0 && x >= m_rx && x < m_rx + m_rw) {
|
Chris@87
|
204 for (size_t y = 0; y < m_height; ++y) {
|
Chris@87
|
205 values[y] = m_range[x - m_rx][y];
|
Chris@87
|
206 }
|
Chris@87
|
207 }
|
Chris@87
|
208
|
Chris@87
|
209 if (!seekTo(x, 0)) return;
|
Chris@87
|
210 if (::read(m_fd, values, m_height * sizeof(float)) != m_height * sizeof(float)) {
|
Chris@87
|
211 ::perror("MatrixFileCache::getColumnAt: read failed");
|
Chris@87
|
212 }
|
Chris@87
|
213 return;
|
Chris@87
|
214 }
|
Chris@87
|
215
|
Chris@87
|
216 void
|
Chris@87
|
217 MatrixFileCache::setValueAt(size_t x, size_t y, float value)
|
Chris@87
|
218 {
|
Chris@87
|
219 if (m_mode != ReadWrite) {
|
Chris@87
|
220 std::cerr << "ERROR: MatrixFileCache::setValueAt called on read-only cache"
|
Chris@87
|
221 << std::endl;
|
Chris@87
|
222 return;
|
Chris@87
|
223 }
|
Chris@87
|
224
|
Chris@87
|
225 if (!seekTo(x, y)) return;
|
Chris@87
|
226 if (::write(m_fd, &value, sizeof(float)) != sizeof(float)) {
|
Chris@87
|
227 ::perror("WARNING: MatrixFileCache::setValueAt: write failed");
|
Chris@87
|
228 }
|
Chris@87
|
229
|
Chris@87
|
230 //... update range as appropriate
|
Chris@87
|
231 }
|
Chris@87
|
232
|
Chris@87
|
233 void
|
Chris@87
|
234 MatrixFileCache::setColumnAt(size_t x, float *values)
|
Chris@87
|
235 {
|
Chris@87
|
236 if (m_mode != ReadWrite) {
|
Chris@87
|
237 std::cerr << "ERROR: MatrixFileCache::setColumnAt called on read-only cache"
|
Chris@87
|
238 << std::endl;
|
Chris@87
|
239 return;
|
Chris@87
|
240 }
|
Chris@87
|
241
|
Chris@87
|
242 if (!seekTo(x, 0)) return;
|
Chris@87
|
243 if (::write(m_fd, values, m_height * sizeof(float)) != m_height * sizeof(float)) {
|
Chris@87
|
244 ::perror("WARNING: MatrixFileCache::setColumnAt: write failed");
|
Chris@87
|
245 }
|
Chris@87
|
246
|
Chris@87
|
247 //... update range as appropriate
|
Chris@87
|
248 }
|
Chris@87
|
249
|
Chris@87
|
250 bool
|
Chris@87
|
251 MatrixFileCache::seekTo(size_t x, size_t y) const
|
Chris@87
|
252 {
|
Chris@87
|
253 off_t off = m_headerSize + (x * m_height + y) * sizeof(float);
|
Chris@87
|
254 if (off == m_off) return true;
|
Chris@87
|
255
|
Chris@87
|
256 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) {
|
Chris@87
|
257 ::perror("Seek failed");
|
Chris@87
|
258 std::cerr << "ERROR: MatrixFileCache::seekTo(" << x << ", " << y
|
Chris@87
|
259 << ") failed" << std::endl;
|
Chris@87
|
260 return false;
|
Chris@87
|
261 }
|
Chris@87
|
262
|
Chris@87
|
263 m_off = off;
|
Chris@87
|
264 return true;
|
Chris@87
|
265 }
|
Chris@87
|
266
|