2#include <Fx/Looper/AudioLayers.hpp>
3#include <Fx/Looper/LooperView.hpp>
5#include <score/document/DocumentContext.hpp>
7#include <core/document/Document.hpp>
8#include <core/document/DocumentModel.hpp>
10#include <ossia/audio/write_loop_wav.hpp>
12#include <boost/container/vector.hpp>
17#include <QStandardPaths>
52 std::array<std::string, slots> paths;
53 std::array<std::string, slots> bases;
61 const QString file = ctx.document.metadata().fileName();
62 if(QFileInfo fi{file}; fi.isAbsolute() && fi.exists())
63 return fi.absolutePath() +
"/Takes";
65 return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)
66 +
"/recordings/" + QString::number(ctx.document.id().val());
70 static std::shared_ptr<take_writer>
make(
72 std::string_view extension)
74 auto w = std::make_shared<take_writer>();
75 w->folder = folder_for(ctx);
76 w->prefix = QString::fromUtf8(prefix.data(), qsizetype(prefix.size()));
77 w->extension = QString::fromUtf8(extension.data(), qsizetype(extension.size()));
78 for(
auto& p : w->paths)
80 for(
auto& p : w->bases)
85 QString base(
int number)
const
87 return QStringLiteral(
"%1/%2-take%3").arg(folder, prefix).arg(number);
89 QString file(
int number,
int layer)
const
91 return layer < 0 ? QStringLiteral(
"%1.%2").arg(base(number), extension)
92 : QStringLiteral(
"%1-layer%2.%3").arg(base(number)).arg(layer + 1).arg(extension);
98 const std::string&
next_path(
int slot,
int& number,
int layer)
102 static std::mutex choosing;
103 std::lock_guard _{choosing};
106 QDir{}.mkpath(folder);
112 const QString head = prefix + QStringLiteral(
"-take");
113 std::vector<int> used;
114 for(
const QString& name :
115 dir.entryList({head + QStringLiteral(
"*")}, QDir::Files | QDir::Hidden))
117 int end = int(head.size());
118 while(end < name.size() && name[end].isDigit())
121 const int n = name.mid(head.size(), end - head.size()).toInt(&ok);
122 if(ok && end < name.size() && (name[end] ==
'.' || name[end] ==
'-'))
125 std::sort(used.begin(), used.end());
133 const QString path = file(number, layer);
134 if(QFile f{path}; f.open(QIODevice::WriteOnly))
136 paths[slot].assign(path.toStdString());
137 bases[slot].assign(base(number).toStdString());
142 const std::string&
take_base(
int slot)
const noexcept {
return bases[slot]; }
143 const std::string& path(
int slot)
const noexcept {
return paths[slot]; }
160 halp::clip_stretch stretch{};
161 const char* extension{};
170 view.set_take(w.
take_base(slot), extension,
true, layers, stretch, placeable);
185 boost::container::vector<float> data;
193 static std::shared_ptr<export_buffer>
make(
int channels, int64_t frames)
195 auto b = std::make_shared<export_buffer>();
196 b->channels = channels;
197 b->capacity = frames;
198 b->data.resize(std::size_t(channels) * std::size_t(frames), boost::container::default_init);
202 float* channel(
int c)
noexcept {
return data.data() + std::size_t(c) * std::size_t(capacity); }
212 std::atomic_bool in_use{};
220 std::shared_ptr<take_writer> writer;
221 std::shared_ptr<export_buffer> audio;
224 std::shared_ptr<audio_stop_take>
stop;
235 int meter_numerator{4};
236 int meter_denominator{4};
238 bool write(
const float*
const* data)
240 const auto& path = writer->next_path(slot,
number, layer);
241 const double quarters = tempo > 0 ? frames / double(rate) * tempo / 60. : 0.;
242 ossia::wav_loop_info info;
244 info.beats = int(std::lround(quarters));
245 info.meter_numerator = meter_numerator;
246 info.meter_denominator = meter_denominator;
248 return ossia::write_loop_wav(path, channels, rate, frames, data, info);
260 ~release() { b.in_use =
false; }
262 if(!writer || channels <= 0 || frames <= 0)
265 std::array<const float*, 16> data{};
266 channels = std::min<int>({channels, audio->channels, int(data.size())});
267 for(
int c = 0; c < channels; c++)
268 data[c] = audio->channel(c);
269 return write(data.data());
283 s.passes.pool.reset();
287 std::array<const float*, 16> data{};
288 channels = std::min<int>({channels, s.passes.channels, int(data.size())});
289 bool ok = writer && s.passes.pool && channels > 0 && frames > 0;
292 std::vector<float> buffer(std::size_t(channels) * std::size_t(frames));
293 const auto channel = [&](
int c) {
return buffer.data() + std::size_t(c) * frames; };
294 for(
int c = 0; c < channels; c++)
295 data[c] = channel(c);
297 for(layer = -1; ok && layer < s.passes.count; layer++)
299 s.passes.flatten(channels, channel, layer);
300 ok = write(data.data());
304 s.notice.written(*writer, slot, ok, s.passes.count,
true);
Definition AudioLayers.hpp:14
Definition AudioLayers.hpp:975
Definition TakeWriter.hpp:209
Definition TakeWriter.hpp:184
static std::shared_ptr< export_buffer > make(int channels, int64_t frames)
Main thread only. The memory is reserved without being written.
Definition TakeWriter.hpp:193
std::atomic_bool in_use
Definition TakeWriter.hpp:190
Definition TakeWriter.hpp:153
view_message view
What the looper showed when it stopped, with its last take.
Definition TakeWriter.hpp:155
void written(take_writer &w, int slot, bool ok, int layers, bool placeable)
Definition TakeWriter.hpp:166
int take
Definition TakeWriter.hpp:158
Definition TakeWriter.hpp:219
bool run_stop()
Definition TakeWriter.hpp:275
int number
Number of the take, 0 for a new one; -1 for the mix, else the layer.
Definition TakeWriter.hpp:228
std::shared_ptr< audio_stop_take > stop
Definition TakeWriter.hpp:224
Definition TakeWriter.hpp:44
std::function< void(const view_message &)> tell_views
Definition TakeWriter.hpp:57
static std::shared_ptr< take_writer > make(const score::DocumentContext &ctx, std::string_view prefix, std::string_view extension)
Main thread only.
Definition TakeWriter.hpp:70
const std::string & next_path(int slot, int &number, int layer)
Definition TakeWriter.hpp:98
const std::string & take_base(int slot) const noexcept
The path of a take's files without the layer and the extension.
Definition TakeWriter.hpp:142
static constexpr int slots
Definition TakeWriter.hpp:47
Definition LooperView.hpp:72
double take_date
Where the last take was recorded, in seconds from the process' start.
Definition LooperView.hpp:101
int take
Takes written so far; the files of the last one are take_files().
Definition LooperView.hpp:86
int version
Definition LooperView.hpp:142
Definition DocumentContext.hpp:18