1{
2 lib,
3 stdenv,
4 alsa-lib,
5 autoPatchelfHook,
6 copyDesktopItems,
7 curl,
8 freetype,
9 htmlq,
10 jq,
11 libglvnd,
12 librsvg,
13 makeDesktopItem,
14 makeWrapper,
15 p7zip,
16 writeShellScript,
17}:
18let
19 versionForFile = v: builtins.replaceStrings [ "." ] [ "" ] v;
20
21 archdirs =
22 if stdenv.hostPlatform.isx86_64 then
23 [
24 "x86-64bit"
25 "amd64"
26 ]
27 else if stdenv.hostPlatform.isAarch64 then
28 [
29 "arm-64bit"
30 "arm"
31 ]
32 else
33 throw "unsupported platform";
34
35 mkPianoteq =
36 {
37 name,
38 mainProgram,
39 startupWMClass,
40 src,
41 version,
42 ...
43 }:
44 stdenv.mkDerivation rec {
45 inherit src version;
46
47 pname = "pianoteq-${name}";
48
49 unpackPhase = ''
50 ${p7zip}/bin/7z x $src
51 '';
52
53 nativeBuildInputs = [
54 autoPatchelfHook
55 copyDesktopItems
56 makeWrapper
57 librsvg
58 ];
59
60 buildInputs = [
61 (lib.getLib stdenv.cc.cc) # libgcc_s.so.1, libstdc++.so.6
62 alsa-lib # libasound.so.2
63 freetype # libfreetype.so.6
64 libglvnd # libGL.so.1
65 ];
66
67 desktopItems = [
68 (makeDesktopItem {
69 name = pname;
70 exec = ''"${mainProgram}"'';
71 desktopName = mainProgram;
72 icon = "pianoteq";
73 comment = meta.description;
74 categories = [
75 "AudioVideo"
76 "Audio"
77 "Recorder"
78 ];
79 startupNotify = false;
80 inherit startupWMClass;
81 })
82 ];
83
84 installPhase = ''
85 runHook preInstall
86 mkdir -p $out/bin
87 mv -t $out/bin ${builtins.concatStringsSep " " (builtins.map (dir: "Pianoteq*/${dir}/*") archdirs)}
88 install -Dm644 ${./pianoteq.svg} $out/share/icons/hicolor/scalable/apps/pianoteq.svg
89 for size in 16 22 32 48 64 128 256; do
90 dir=$out/share/icons/hicolor/"$size"x"$size"/apps
91 mkdir -p $dir
92 rsvg-convert \
93 --keep-aspect-ratio \
94 --width $size \
95 --height $size \
96 --output $dir/pianoteq.png \
97 ${./pianoteq.svg}
98 done
99 runHook postInstall
100 '';
101
102 meta = with lib; {
103 homepage = "https://www.modartt.com/pianoteq";
104 description = "Software synthesizer that features real-time MIDI-control of digital physically modeled pianos and related instruments";
105 license = licenses.unfree;
106 inherit mainProgram;
107 platforms = [
108 "x86_64-linux"
109 "aarch64-linux"
110 ];
111 maintainers = with maintainers; [
112 mausch
113 ners
114 ];
115 sourceProvenance = [ lib.sourceTypes.binaryNativeCode ];
116 };
117 };
118
119 fetchWithCurlScript =
120 {
121 name,
122 hash,
123 script,
124 impureEnvVars ? [ ],
125 }:
126 stdenv.mkDerivation {
127 inherit name;
128 builder = writeShellScript "builder.sh" ''
129 curlVersion=$(${curl}/bin/curl -V | head -1 | cut -d' ' -f2)
130
131 # Curl flags to handle redirects, not use EPSV, handle cookies for
132 # servers to need them during redirects, and work on SSL without a
133 # certificate (this isn't a security problem because we check the
134 # cryptographic hash of the output anyway).
135 curl=(
136 ${curl}/bin/curl
137 --location
138 --max-redirs 20
139 --retry 3
140 --disable-epsv
141 --cookie-jar cookies
142 --insecure
143 --user-agent "curl/$curlVersion Nixpkgs/${lib.trivial.release}"
144 $NIX_CURL_FLAGS
145 )
146
147 ${script}
148
149 '';
150 nativeBuildInputs = [ curl ];
151 outputHashAlgo = "sha256";
152 outputHash = hash;
153
154 impureEnvVars =
155 lib.fetchers.proxyImpureEnvVars
156 ++ impureEnvVars
157 ++ [
158 # This variable allows the user to pass additional options to curl
159 "NIX_CURL_FLAGS"
160 ];
161 };
162
163 fetchPianoteqTrial =
164 { name, hash }:
165 fetchWithCurlScript {
166 inherit name hash;
167 script = ''
168 html=$(
169 "''${curl[@]}" --silent --request GET \
170 --cookie cookies \
171 --header "accept: */*" \
172 'https://www.modartt.com/try?file=${name}'
173 )
174
175 signature="$(echo "$html" | ${htmlq}/bin/htmlq '#download-form' --attribute action | cut -f2 -d'&' | cut -f2 -d=)"
176
177 json=$(
178 "''${curl[@]}" --silent --request POST \
179 --cookie cookies \
180 --header "modartt-json: request" \
181 --header "origin: https://www.modartt.com" \
182 --header "content-type: application/json; charset=UTF-8" \
183 --header "accept: application/json, text/javascript, */*" \
184 --data-raw '{"file": "${name}", "get": "url", "signature": "'"$signature"'"}' \
185 https://www.modartt.com/api/0/download
186 )
187
188 url=$(echo $json | ${jq}/bin/jq -r .url)
189 if [ "$url" == "null" ]; then
190 echo "Could not get download URL, open an issue on https://github.com/NixOS/nixpkgs"
191 return 1
192 fi
193 "''${curl[@]}" --progress-bar --cookie cookies -o $out "$url"
194 '';
195 };
196
197 fetchPianoteqWithLogin =
198 { name, hash }:
199 fetchWithCurlScript {
200 inherit name hash;
201
202 impureEnvVars = [
203 "NIX_MODARTT_USERNAME"
204 "NIX_MODARTT_PASSWORD"
205 ];
206
207 script = ''
208 if [ -z "''${NIX_MODARTT_USERNAME}" -o -z "''${NIX_MODARTT_PASSWORD}" ]; then
209 echo "Error: Downloading a personal Pianoteq instance requires the nix building process (nix-daemon in multi user mode) to have the NIX_MODARTT_USERNAME and NIX_MODARTT_PASSWORD env vars set." >&2
210 exit 1
211 fi
212
213 "''${curl[@]}" -s -o /dev/null "https://www.modartt.com/user_area"
214
215 ${jq}/bin/jq -n "{connect: 1, login: \"''${NIX_MODARTT_USERNAME}\", password: \"''${NIX_MODARTT_PASSWORD}\"}" > login.json
216
217 "''${curl[@]}" --silent --request POST \
218 --cookie cookies \
219 --referer "https://www.modartt.com/user_area" \
220 --header "modartt-json: request" \
221 --header "origin: https://www.modartt.com" \
222 --header "content-type: application/json; charset=UTF-8" \
223 --header "accept: application/json, text/javascript, */*" \
224 --data @login.json \
225 https://www.modartt.com/api/0/session
226
227 json=$(
228 "''${curl[@]}" --silent --request POST \
229 --cookie cookies \
230 --header "modartt-json: request" \
231 --header "origin: https://www.modartt.com" \
232 --header "content-type: application/json; charset=UTF-8" \
233 --header "accept: application/json, text/javascript, */*" \
234 --data-raw '{"file": "${name}", "get": "url"}' \
235 https://www.modartt.com/api/0/download
236 )
237
238 url=$(echo $json | ${jq}/bin/jq -r .url)
239 "''${curl[@]}" --progress-bar --cookie cookies -o $out "$url"
240 '';
241 };
242
243 version6 = "6.7.3";
244 version7 = "7.5.4";
245 version8 = "8.4.0";
246
247 mkStandard =
248 version: hash:
249 mkPianoteq {
250 name = "standard";
251 mainProgram = "Pianoteq ${lib.versions.major version}";
252 startupWMClass = "Pianoteq";
253 inherit version;
254 src = fetchPianoteqWithLogin {
255 name = "pianoteq_linux_v${versionForFile version}.7z";
256 inherit hash;
257 };
258 };
259 mkStage =
260 version: hash:
261 mkPianoteq {
262 name = "stage";
263 mainProgram = "Pianoteq ${lib.versions.major version} STAGE";
264 startupWMClass = "Pianoteq STAGE";
265 inherit version;
266 src = fetchPianoteqWithLogin {
267 name = "pianoteq_stage_linux_v${versionForFile version}.7z";
268 inherit hash;
269 };
270 };
271 mkStandardTrial =
272 version: hash:
273 mkPianoteq {
274 name = "standard-trial";
275 mainProgram = "Pianoteq ${lib.versions.major version}";
276 startupWMClass = "Pianoteq Trial";
277 inherit version;
278 src = fetchPianoteqTrial {
279 name = "pianoteq_linux_trial_v${versionForFile version}.7z";
280 inherit hash;
281 };
282 };
283 mkStageTrial =
284 version: hash:
285 mkPianoteq {
286 name = "stage-trial";
287 mainProgram = "Pianoteq ${lib.versions.major version} STAGE";
288 startupWMClass = "Pianoteq STAGE Trial";
289 inherit version;
290 src = fetchPianoteqTrial {
291 name = "pianoteq_stage_linux_trial_v${versionForFile version}.7z";
292 inherit hash;
293 };
294 };
295in
296{
297 standard_8 = mkStandard version8 "sha256-ZDGB/SOOz+sWz7P+sNzyaipEH452n8zq5LleO3ztSXc=";
298 stage_8 = mkStage version8 "";
299 standard-trial_8 = mkStandardTrial version8 "sha256-K3LbAWxciXt9hVAyRayxSoE/IYJ38Fd03+j0s7ZsMuw=";
300 stage-trial_8 = mkStageTrial version8 "sha256-k0p7SnkEq90bqIlT7PTYAQuhKEDVi+srHwYrpMUtIbM=";
301
302 standard_7 = mkStandard version7 "sha256-TA9CiuT21fQedlMUGz7bNNxYun5ArmRjvIxjOGqXDCs=";
303 stage_7 = mkStage version7 "";
304 standard-trial_7 = mkStandardTrial version7 "sha256-3a3+SKTEhvDtqK5Kg4E6KiLvn5+j6JN6ntIb72u2bdQ=";
305 stage-trial_7 = mkStageTrial version7 "sha256-ybtq+hjnaQxpLxv2KE0ZcbQXtn5DJJsnMwCmh3rlrIc=";
306
307 standard_6 = mkStandard version6 "sha256-u6ZNpmHFVOk+r+6Q8OURSfAi41cxMoDvaEXrTtHEAVY=";
308 stage_6 = mkStage version6 "";
309 standard-trial_6 = mkStandardTrial version6 "sha256-nHTAqosOJqC0VnRw2/xVpZ6y02vvau6CgfNmgiN/AHs=";
310 stage-trial_6 = mkStageTrial version6 "sha256-zrv0c/Mxt1EysR7ZvmxtksXAF5MyXTFMNj4KAdO3QnE=";
311}