@@ -278,6 +278,21 @@ struct MomentarySwitch : virtual Switch { | |||
} | |||
}; | |||
//////////////////// | |||
// IO widgets | |||
//////////////////// | |||
struct AudioIO; | |||
struct AudioWidget : OpaqueWidget { | |||
/** Not owned */ | |||
AudioIO *audioIO = NULL; | |||
void onMouseDown(EventMouseDown &e) override; | |||
}; | |||
struct MIDIWidget : OpaqueWidget { | |||
}; | |||
//////////////////// | |||
// lights | |||
//////////////////// | |||
@@ -0,0 +1,42 @@ | |||
#pragma once | |||
#pragma GCC diagnostic push | |||
#pragma GCC diagnostic ignored "-Wsuggest-override" | |||
#include <RtAudio.h> | |||
#pragma GCC diagnostic pop | |||
namespace rack { | |||
struct AudioIO { | |||
int maxOutputs = 8; | |||
int maxInputs = 8; | |||
RtAudio *stream = NULL; | |||
// Stream properties | |||
int device = -1; | |||
int sampleRate = 44100; | |||
int blockSize = 256; | |||
int numOutputs = 0; | |||
int numInputs = 0; | |||
AudioIO(); | |||
~AudioIO(); | |||
std::vector<int> listDrivers(); | |||
std::string getDriverName(int driver); | |||
int getDriver(); | |||
void setDriver(int driver); | |||
int getDeviceCount(); | |||
std::string getDeviceName(int device); | |||
std::string getDeviceDetail(int device); | |||
void openStream(); | |||
void closeStream(); | |||
std::vector<int> listSampleRates(); | |||
}; | |||
} // namespace rack |
@@ -336,6 +336,22 @@ struct BefacoSlidePot : SVGFader { | |||
} | |||
}; | |||
//////////////////// | |||
// IO widgets | |||
//////////////////// | |||
struct USB_B_AudioWidget : AudioWidget, SVGWidget { | |||
USB_B_AudioWidget() { | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/USB-B.svg"))); | |||
} | |||
}; | |||
struct DIN_MIDIWidget : MIDIWidget, SVGWidget { | |||
DIN_MIDIWidget() { | |||
setSVG(SVG::load(assetGlobal("res/ComponentLibrary/MIDI_DIN.svg"))); | |||
} | |||
}; | |||
//////////////////// | |||
// Jacks | |||
//////////////////// |
@@ -7,7 +7,7 @@ | |||
#include "engine.hpp" | |||
#include "gui.hpp" | |||
#include "app.hpp" | |||
#include "components.hpp" | |||
#include "componentlibrary.hpp" | |||
namespace rack { | |||
@@ -25,6 +25,9 @@ inline Vec mm2px(Vec millimeters) { | |||
return millimeters.mult(SVG_DPI / 25.4); | |||
} | |||
#define CHECKMARK_STRING "âś”" | |||
#define CHECKMARK(_cond) ((_cond) ? CHECKMARK_STRING : "") | |||
//////////////////// | |||
// resources | |||
@@ -0,0 +1,366 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="10.415202mm" | |||
height="10.667406mm" | |||
viewBox="0 0 10.415202 10.667406" | |||
version="1.1" | |||
id="svg8104" | |||
inkscape:version="0.92.2 5c3e80d, 2017-08-06" | |||
sodipodi:docname="ADAT.svg"> | |||
<defs | |||
id="defs8098"> | |||
<clipPath | |||
id="clip306"> | |||
<path | |||
d="m 1550,2365 h 44 v 44 h -44 z m 0,0" | |||
id="path6964" | |||
inkscape:connector-curvature="0" /> | |||
</clipPath> | |||
<clipPath | |||
id="clip307"> | |||
<path | |||
d="m 1550.0859,2386.6914 c 0,11.8867 9.6368,21.5274 21.5274,21.5274 11.8906,0 21.5273,-9.6407 21.5273,-21.5274 0,-11.8906 -9.6367,-21.5312 -21.5273,-21.5312 -11.8906,0 -21.5274,9.6406 -21.5274,21.5312" | |||
id="path6967" | |||
inkscape:connector-curvature="0" /> | |||
</clipPath> | |||
<linearGradient | |||
id="linear3" | |||
gradientUnits="userSpaceOnUse" | |||
x1="-0.0115906" | |||
y1="0" | |||
x2="0.985816" | |||
y2="0" | |||
gradientTransform="matrix(0,43.168976,-43.168976,0,1571.6138,2365.6624)"> | |||
<stop | |||
offset="0" | |||
style="stop-color:rgb(32.940674%,32.940674%,33.724976%);stop-opacity:1;" | |||
id="stop6970" /> | |||
<stop | |||
offset="0.03125" | |||
style="stop-color:rgb(32.814026%,32.806396%,33.586121%);stop-opacity:1;" | |||
id="stop6972" /> | |||
<stop | |||
offset="0.046875" | |||
style="stop-color:rgb(32.58667%,32.565308%,33.338928%);stop-opacity:1;" | |||
id="stop6974" /> | |||
<stop | |||
offset="0.0625" | |||
style="stop-color:rgb(32.383728%,32.350159%,33.117676%);stop-opacity:1;" | |||
id="stop6976" /> | |||
<stop | |||
offset="0.078125" | |||
style="stop-color:rgb(32.182312%,32.136536%,32.897949%);stop-opacity:1;" | |||
id="stop6978" /> | |||
<stop | |||
offset="0.09375" | |||
style="stop-color:rgb(31.980896%,31.922913%,32.678223%);stop-opacity:1;" | |||
id="stop6980" /> | |||
<stop | |||
offset="0.109375" | |||
style="stop-color:rgb(31.77948%,31.70929%,32.458496%);stop-opacity:1;" | |||
id="stop6982" /> | |||
<stop | |||
offset="0.125" | |||
style="stop-color:rgb(31.578064%,31.495667%,32.23877%);stop-opacity:1;" | |||
id="stop6984" /> | |||
<stop | |||
offset="0.140625" | |||
style="stop-color:rgb(31.376648%,31.280518%,32.017517%);stop-opacity:1;" | |||
id="stop6986" /> | |||
<stop | |||
offset="0.15625" | |||
style="stop-color:rgb(31.173706%,31.066895%,31.797791%);stop-opacity:1;" | |||
id="stop6988" /> | |||
<stop | |||
offset="0.171875" | |||
style="stop-color:rgb(30.97229%,30.853271%,31.578064%);stop-opacity:1;" | |||
id="stop6990" /> | |||
<stop | |||
offset="0.1875" | |||
style="stop-color:rgb(30.770874%,30.639648%,31.358337%);stop-opacity:1;" | |||
id="stop6992" /> | |||
<stop | |||
offset="0.203125" | |||
style="stop-color:rgb(30.569458%,30.426025%,31.138611%);stop-opacity:1;" | |||
id="stop6994" /> | |||
<stop | |||
offset="0.21875" | |||
style="stop-color:rgb(30.368042%,30.212402%,30.917358%);stop-opacity:1;" | |||
id="stop6996" /> | |||
<stop | |||
offset="0.234375" | |||
style="stop-color:rgb(30.166626%,29.997253%,30.697632%);stop-opacity:1;" | |||
id="stop6998" /> | |||
<stop | |||
offset="0.25" | |||
style="stop-color:rgb(29.963684%,29.78363%,30.477905%);stop-opacity:1;" | |||
id="stop7000" /> | |||
<stop | |||
offset="0.265625" | |||
style="stop-color:rgb(29.762268%,29.570007%,30.258179%);stop-opacity:1;" | |||
id="stop7002" /> | |||
<stop | |||
offset="0.28125" | |||
style="stop-color:rgb(29.560852%,29.356384%,30.038452%);stop-opacity:1;" | |||
id="stop7004" /> | |||
<stop | |||
offset="0.296875" | |||
style="stop-color:rgb(29.359436%,29.142761%,29.8172%);stop-opacity:1;" | |||
id="stop7006" /> | |||
<stop | |||
offset="0.3125" | |||
style="stop-color:rgb(29.15802%,28.927612%,29.597473%);stop-opacity:1;" | |||
id="stop7008" /> | |||
<stop | |||
offset="0.328125" | |||
style="stop-color:rgb(28.955078%,28.713989%,29.377747%);stop-opacity:1;" | |||
id="stop7010" /> | |||
<stop | |||
offset="0.34375" | |||
style="stop-color:rgb(28.753662%,28.500366%,29.15802%);stop-opacity:1;" | |||
id="stop7012" /> | |||
<stop | |||
offset="0.359375" | |||
style="stop-color:rgb(28.552246%,28.286743%,28.938293%);stop-opacity:1;" | |||
id="stop7014" /> | |||
<stop | |||
offset="0.375" | |||
style="stop-color:rgb(28.35083%,28.07312%,28.718567%);stop-opacity:1;" | |||
id="stop7016" /> | |||
<stop | |||
offset="0.390625" | |||
style="stop-color:rgb(28.149414%,27.857971%,28.497314%);stop-opacity:1;" | |||
id="stop7018" /> | |||
<stop | |||
offset="0.40625" | |||
style="stop-color:rgb(27.947998%,27.644348%,28.277588%);stop-opacity:1;" | |||
id="stop7020" /> | |||
<stop | |||
offset="0.421875" | |||
style="stop-color:rgb(27.745056%,27.430725%,28.057861%);stop-opacity:1;" | |||
id="stop7022" /> | |||
<stop | |||
offset="0.4375" | |||
style="stop-color:rgb(27.54364%,27.217102%,27.838135%);stop-opacity:1;" | |||
id="stop7024" /> | |||
<stop | |||
offset="0.453125" | |||
style="stop-color:rgb(27.342224%,27.003479%,27.618408%);stop-opacity:1;" | |||
id="stop7026" /> | |||
<stop | |||
offset="0.46875" | |||
style="stop-color:rgb(27.140808%,26.789856%,27.397156%);stop-opacity:1;" | |||
id="stop7028" /> | |||
<stop | |||
offset="0.484375" | |||
style="stop-color:rgb(26.939392%,26.574707%,27.177429%);stop-opacity:1;" | |||
id="stop7030" /> | |||
<stop | |||
offset="0.5" | |||
style="stop-color:rgb(26.737976%,26.361084%,26.957703%);stop-opacity:1;" | |||
id="stop7032" /> | |||
<stop | |||
offset="0.515625" | |||
style="stop-color:rgb(26.535034%,26.147461%,26.737976%);stop-opacity:1;" | |||
id="stop7034" /> | |||
<stop | |||
offset="0.53125" | |||
style="stop-color:rgb(26.333618%,25.933838%,26.51825%);stop-opacity:1;" | |||
id="stop7036" /> | |||
<stop | |||
offset="0.546875" | |||
style="stop-color:rgb(26.132202%,25.720215%,26.296997%);stop-opacity:1;" | |||
id="stop7038" /> | |||
<stop | |||
offset="0.5625" | |||
style="stop-color:rgb(25.930786%,25.505066%,26.077271%);stop-opacity:1;" | |||
id="stop7040" /> | |||
<stop | |||
offset="0.578125" | |||
style="stop-color:rgb(25.72937%,25.291443%,25.857544%);stop-opacity:1;" | |||
id="stop7042" /> | |||
<stop | |||
offset="0.59375" | |||
style="stop-color:rgb(25.527954%,25.07782%,25.637817%);stop-opacity:1;" | |||
id="stop7044" /> | |||
<stop | |||
offset="0.609375" | |||
style="stop-color:rgb(25.325012%,24.864197%,25.418091%);stop-opacity:1;" | |||
id="stop7046" /> | |||
<stop | |||
offset="0.625" | |||
style="stop-color:rgb(25.123596%,24.650574%,25.196838%);stop-opacity:1;" | |||
id="stop7048" /> | |||
<stop | |||
offset="0.640625" | |||
style="stop-color:rgb(24.92218%,24.436951%,24.977112%);stop-opacity:1;" | |||
id="stop7050" /> | |||
<stop | |||
offset="0.65625" | |||
style="stop-color:rgb(24.720764%,24.221802%,24.757385%);stop-opacity:1;" | |||
id="stop7052" /> | |||
<stop | |||
offset="0.671875" | |||
style="stop-color:rgb(24.519348%,24.008179%,24.537659%);stop-opacity:1;" | |||
id="stop7054" /> | |||
<stop | |||
offset="0.6875" | |||
style="stop-color:rgb(24.316406%,23.794556%,24.317932%);stop-opacity:1;" | |||
id="stop7056" /> | |||
<stop | |||
offset="0.703125" | |||
style="stop-color:rgb(24.11499%,23.580933%,24.098206%);stop-opacity:1;" | |||
id="stop7058" /> | |||
<stop | |||
offset="0.71875" | |||
style="stop-color:rgb(23.913574%,23.36731%,23.876953%);stop-opacity:1;" | |||
id="stop7060" /> | |||
<stop | |||
offset="0.734375" | |||
style="stop-color:rgb(23.712158%,23.152161%,23.657227%);stop-opacity:1;" | |||
id="stop7062" /> | |||
<stop | |||
offset="0.75" | |||
style="stop-color:rgb(23.510742%,22.938538%,23.4375%);stop-opacity:1;" | |||
id="stop7064" /> | |||
<stop | |||
offset="0.765625" | |||
style="stop-color:rgb(23.309326%,22.724915%,23.217773%);stop-opacity:1;" | |||
id="stop7066" /> | |||
<stop | |||
offset="0.78125" | |||
style="stop-color:rgb(23.106384%,22.511292%,22.998047%);stop-opacity:1;" | |||
id="stop7068" /> | |||
<stop | |||
offset="0.796875" | |||
style="stop-color:rgb(22.904968%,22.297668%,22.776794%);stop-opacity:1;" | |||
id="stop7070" /> | |||
<stop | |||
offset="0.8125" | |||
style="stop-color:rgb(22.703552%,22.084045%,22.557068%);stop-opacity:1;" | |||
id="stop7072" /> | |||
<stop | |||
offset="0.828125" | |||
style="stop-color:rgb(22.502136%,21.868896%,22.337341%);stop-opacity:1;" | |||
id="stop7074" /> | |||
<stop | |||
offset="0.84375" | |||
style="stop-color:rgb(22.30072%,21.655273%,22.117615%);stop-opacity:1;" | |||
id="stop7076" /> | |||
<stop | |||
offset="0.859375" | |||
style="stop-color:rgb(22.099304%,21.44165%,21.897888%);stop-opacity:1;" | |||
id="stop7078" /> | |||
<stop | |||
offset="0.875" | |||
style="stop-color:rgb(21.896362%,21.228027%,21.676636%);stop-opacity:1;" | |||
id="stop7080" /> | |||
<stop | |||
offset="0.890625" | |||
style="stop-color:rgb(21.694946%,21.014404%,21.456909%);stop-opacity:1;" | |||
id="stop7082" /> | |||
<stop | |||
offset="0.90625" | |||
style="stop-color:rgb(21.49353%,20.799255%,21.237183%);stop-opacity:1;" | |||
id="stop7084" /> | |||
<stop | |||
offset="0.921875" | |||
style="stop-color:rgb(21.292114%,20.585632%,21.017456%);stop-opacity:1;" | |||
id="stop7086" /> | |||
<stop | |||
offset="0.9375" | |||
style="stop-color:rgb(21.090698%,20.372009%,20.797729%);stop-opacity:1;" | |||
id="stop7088" /> | |||
<stop | |||
offset="0.953125" | |||
style="stop-color:rgb(20.889282%,20.158386%,20.576477%);stop-opacity:1;" | |||
id="stop7090" /> | |||
<stop | |||
offset="0.96875" | |||
style="stop-color:rgb(20.68634%,19.944763%,20.35675%);stop-opacity:1;" | |||
id="stop7092" /> | |||
<stop | |||
offset="0.984375" | |||
style="stop-color:rgb(20.484924%,19.729614%,20.137024%);stop-opacity:1;" | |||
id="stop7094" /> | |||
<stop | |||
offset="1" | |||
style="stop-color:rgb(20.283508%,19.515991%,19.917297%);stop-opacity:1;" | |||
id="stop7096" /> | |||
</linearGradient> | |||
</defs> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="1.979899" | |||
inkscape:cx="171.96332" | |||
inkscape:cy="-12.111977" | |||
inkscape:document-units="mm" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
fit-margin-top="0" | |||
fit-margin-left="0" | |||
fit-margin-right="0" | |||
fit-margin-bottom="0" | |||
inkscape:window-width="1600" | |||
inkscape:window-height="882" | |||
inkscape:window-x="0" | |||
inkscape:window-y="18" | |||
inkscape:window-maximized="0" /> | |||
<metadata | |||
id="metadata8101"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title></dc:title> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(-98.481578,-96.351604)"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10049" | |||
d="m 108.89678,106.51189 c 0,0.27838 -0.22874,0.50712 -0.50849,0.50712 h -9.398222 c -0.27837,0 -0.50849,-0.22874 -0.50849,-0.50712 v -9.398215 c 0,-0.279753 0.23012,-0.507119 0.50849,-0.507119 h 9.398222 c 0.27975,0 0.50849,0.227366 0.50849,0.507119 z m 0,0" | |||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10051" | |||
d="m 108.89678,106.25831 c 0,0.27838 -0.22874,0.50712 -0.50849,0.50712 h -9.398222 c -0.27837,0 -0.50849,-0.22874 -0.50849,-0.50712 v -9.398212 c 0,-0.279718 0.23012,-0.508494 0.50849,-0.508494 h 9.398222 c 0.27975,0 0.50849,0.228776 0.50849,0.508494 z m 0,0" | |||
style="fill:#424242;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10053" | |||
d="m 106.72501,98.389742 h -3.61735 l -0.55813,0.617361 h -0.82956 l -1.06662,1.014237 v 3.07164 l 1.06662,1.06521 h 0.82956 l 0.55813,0.57051 h 3.61735 z m 0,0" | |||
style="fill:#211e1e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10055" | |||
d="m 103.00155,104.98228 -0.5581,-0.56914 h -0.82959 l -1.21405,-1.21542 v -3.285248 l 1.21818,-1.158945 h 0.81855 l 0.5595,-0.617361 h 3.98251 v 6.846114 z m 0,0" | |||
style="fill:#211e1e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10057" | |||
d="m 106.72501,98.389742 h -3.61735 l -0.55813,0.617361 h -0.82956 l -1.06662,1.014237 v 3.07164 l 1.06662,1.06521 h 0.82956 l 0.55813,0.57051 h 3.61735 z m 0,0" | |||
style="fill:#424242;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,397 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:xlink="http://www.w3.org/1999/xlink" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="16.999479mm" | |||
height="17.000858mm" | |||
viewBox="0 0 16.999479 17.000858" | |||
version="1.1" | |||
id="svg8104" | |||
inkscape:version="0.92.2 5c3e80d, 2017-08-06" | |||
sodipodi:docname="MIDI_DIN.svg"> | |||
<defs | |||
id="defs8098"> | |||
<linearGradient | |||
inkscape:collect="always" | |||
id="linearGradient1258"> | |||
<stop | |||
style="stop-color:#545456;stop-opacity:1" | |||
offset="0" | |||
id="stop1254" /> | |||
<stop | |||
style="stop-color:#333132;stop-opacity:1" | |||
offset="1" | |||
id="stop1256" /> | |||
</linearGradient> | |||
<clipPath | |||
id="clip306"> | |||
<path | |||
d="m 1550,2365 h 44 v 44 h -44 z m 0,0" | |||
id="path6964" | |||
inkscape:connector-curvature="0" /> | |||
</clipPath> | |||
<linearGradient | |||
id="linear3" | |||
gradientUnits="userSpaceOnUse" | |||
x1="-0.0115906" | |||
y1="0" | |||
x2="0.985816" | |||
y2="0" | |||
gradientTransform="matrix(0,43.168976,-43.168976,0,1571.6138,2365.6624)"> | |||
<stop | |||
offset="0" | |||
style="stop-color:rgb(32.940674%,32.940674%,33.724976%);stop-opacity:1;" | |||
id="stop6970" /> | |||
<stop | |||
offset="0.03125" | |||
style="stop-color:rgb(32.814026%,32.806396%,33.586121%);stop-opacity:1;" | |||
id="stop6972" /> | |||
<stop | |||
offset="0.046875" | |||
style="stop-color:rgb(32.58667%,32.565308%,33.338928%);stop-opacity:1;" | |||
id="stop6974" /> | |||
<stop | |||
offset="0.0625" | |||
style="stop-color:rgb(32.383728%,32.350159%,33.117676%);stop-opacity:1;" | |||
id="stop6976" /> | |||
<stop | |||
offset="0.078125" | |||
style="stop-color:rgb(32.182312%,32.136536%,32.897949%);stop-opacity:1;" | |||
id="stop6978" /> | |||
<stop | |||
offset="0.09375" | |||
style="stop-color:rgb(31.980896%,31.922913%,32.678223%);stop-opacity:1;" | |||
id="stop6980" /> | |||
<stop | |||
offset="0.109375" | |||
style="stop-color:rgb(31.77948%,31.70929%,32.458496%);stop-opacity:1;" | |||
id="stop6982" /> | |||
<stop | |||
offset="0.125" | |||
style="stop-color:rgb(31.578064%,31.495667%,32.23877%);stop-opacity:1;" | |||
id="stop6984" /> | |||
<stop | |||
offset="0.140625" | |||
style="stop-color:rgb(31.376648%,31.280518%,32.017517%);stop-opacity:1;" | |||
id="stop6986" /> | |||
<stop | |||
offset="0.15625" | |||
style="stop-color:rgb(31.173706%,31.066895%,31.797791%);stop-opacity:1;" | |||
id="stop6988" /> | |||
<stop | |||
offset="0.171875" | |||
style="stop-color:rgb(30.97229%,30.853271%,31.578064%);stop-opacity:1;" | |||
id="stop6990" /> | |||
<stop | |||
offset="0.1875" | |||
style="stop-color:rgb(30.770874%,30.639648%,31.358337%);stop-opacity:1;" | |||
id="stop6992" /> | |||
<stop | |||
offset="0.203125" | |||
style="stop-color:rgb(30.569458%,30.426025%,31.138611%);stop-opacity:1;" | |||
id="stop6994" /> | |||
<stop | |||
offset="0.21875" | |||
style="stop-color:rgb(30.368042%,30.212402%,30.917358%);stop-opacity:1;" | |||
id="stop6996" /> | |||
<stop | |||
offset="0.234375" | |||
style="stop-color:rgb(30.166626%,29.997253%,30.697632%);stop-opacity:1;" | |||
id="stop6998" /> | |||
<stop | |||
offset="0.25" | |||
style="stop-color:rgb(29.963684%,29.78363%,30.477905%);stop-opacity:1;" | |||
id="stop7000" /> | |||
<stop | |||
offset="0.265625" | |||
style="stop-color:rgb(29.762268%,29.570007%,30.258179%);stop-opacity:1;" | |||
id="stop7002" /> | |||
<stop | |||
offset="0.28125" | |||
style="stop-color:rgb(29.560852%,29.356384%,30.038452%);stop-opacity:1;" | |||
id="stop7004" /> | |||
<stop | |||
offset="0.296875" | |||
style="stop-color:rgb(29.359436%,29.142761%,29.8172%);stop-opacity:1;" | |||
id="stop7006" /> | |||
<stop | |||
offset="0.3125" | |||
style="stop-color:rgb(29.15802%,28.927612%,29.597473%);stop-opacity:1;" | |||
id="stop7008" /> | |||
<stop | |||
offset="0.328125" | |||
style="stop-color:rgb(28.955078%,28.713989%,29.377747%);stop-opacity:1;" | |||
id="stop7010" /> | |||
<stop | |||
offset="0.34375" | |||
style="stop-color:rgb(28.753662%,28.500366%,29.15802%);stop-opacity:1;" | |||
id="stop7012" /> | |||
<stop | |||
offset="0.359375" | |||
style="stop-color:rgb(28.552246%,28.286743%,28.938293%);stop-opacity:1;" | |||
id="stop7014" /> | |||
<stop | |||
offset="0.375" | |||
style="stop-color:rgb(28.35083%,28.07312%,28.718567%);stop-opacity:1;" | |||
id="stop7016" /> | |||
<stop | |||
offset="0.390625" | |||
style="stop-color:rgb(28.149414%,27.857971%,28.497314%);stop-opacity:1;" | |||
id="stop7018" /> | |||
<stop | |||
offset="0.40625" | |||
style="stop-color:rgb(27.947998%,27.644348%,28.277588%);stop-opacity:1;" | |||
id="stop7020" /> | |||
<stop | |||
offset="0.421875" | |||
style="stop-color:rgb(27.745056%,27.430725%,28.057861%);stop-opacity:1;" | |||
id="stop7022" /> | |||
<stop | |||
offset="0.4375" | |||
style="stop-color:rgb(27.54364%,27.217102%,27.838135%);stop-opacity:1;" | |||
id="stop7024" /> | |||
<stop | |||
offset="0.453125" | |||
style="stop-color:rgb(27.342224%,27.003479%,27.618408%);stop-opacity:1;" | |||
id="stop7026" /> | |||
<stop | |||
offset="0.46875" | |||
style="stop-color:rgb(27.140808%,26.789856%,27.397156%);stop-opacity:1;" | |||
id="stop7028" /> | |||
<stop | |||
offset="0.484375" | |||
style="stop-color:rgb(26.939392%,26.574707%,27.177429%);stop-opacity:1;" | |||
id="stop7030" /> | |||
<stop | |||
offset="0.5" | |||
style="stop-color:rgb(26.737976%,26.361084%,26.957703%);stop-opacity:1;" | |||
id="stop7032" /> | |||
<stop | |||
offset="0.515625" | |||
style="stop-color:rgb(26.535034%,26.147461%,26.737976%);stop-opacity:1;" | |||
id="stop7034" /> | |||
<stop | |||
offset="0.53125" | |||
style="stop-color:rgb(26.333618%,25.933838%,26.51825%);stop-opacity:1;" | |||
id="stop7036" /> | |||
<stop | |||
offset="0.546875" | |||
style="stop-color:rgb(26.132202%,25.720215%,26.296997%);stop-opacity:1;" | |||
id="stop7038" /> | |||
<stop | |||
offset="0.5625" | |||
style="stop-color:rgb(25.930786%,25.505066%,26.077271%);stop-opacity:1;" | |||
id="stop7040" /> | |||
<stop | |||
offset="0.578125" | |||
style="stop-color:rgb(25.72937%,25.291443%,25.857544%);stop-opacity:1;" | |||
id="stop7042" /> | |||
<stop | |||
offset="0.59375" | |||
style="stop-color:rgb(25.527954%,25.07782%,25.637817%);stop-opacity:1;" | |||
id="stop7044" /> | |||
<stop | |||
offset="0.609375" | |||
style="stop-color:rgb(25.325012%,24.864197%,25.418091%);stop-opacity:1;" | |||
id="stop7046" /> | |||
<stop | |||
offset="0.625" | |||
style="stop-color:rgb(25.123596%,24.650574%,25.196838%);stop-opacity:1;" | |||
id="stop7048" /> | |||
<stop | |||
offset="0.640625" | |||
style="stop-color:rgb(24.92218%,24.436951%,24.977112%);stop-opacity:1;" | |||
id="stop7050" /> | |||
<stop | |||
offset="0.65625" | |||
style="stop-color:rgb(24.720764%,24.221802%,24.757385%);stop-opacity:1;" | |||
id="stop7052" /> | |||
<stop | |||
offset="0.671875" | |||
style="stop-color:rgb(24.519348%,24.008179%,24.537659%);stop-opacity:1;" | |||
id="stop7054" /> | |||
<stop | |||
offset="0.6875" | |||
style="stop-color:rgb(24.316406%,23.794556%,24.317932%);stop-opacity:1;" | |||
id="stop7056" /> | |||
<stop | |||
offset="0.703125" | |||
style="stop-color:rgb(24.11499%,23.580933%,24.098206%);stop-opacity:1;" | |||
id="stop7058" /> | |||
<stop | |||
offset="0.71875" | |||
style="stop-color:rgb(23.913574%,23.36731%,23.876953%);stop-opacity:1;" | |||
id="stop7060" /> | |||
<stop | |||
offset="0.734375" | |||
style="stop-color:rgb(23.712158%,23.152161%,23.657227%);stop-opacity:1;" | |||
id="stop7062" /> | |||
<stop | |||
offset="0.75" | |||
style="stop-color:rgb(23.510742%,22.938538%,23.4375%);stop-opacity:1;" | |||
id="stop7064" /> | |||
<stop | |||
offset="0.765625" | |||
style="stop-color:rgb(23.309326%,22.724915%,23.217773%);stop-opacity:1;" | |||
id="stop7066" /> | |||
<stop | |||
offset="0.78125" | |||
style="stop-color:rgb(23.106384%,22.511292%,22.998047%);stop-opacity:1;" | |||
id="stop7068" /> | |||
<stop | |||
offset="0.796875" | |||
style="stop-color:rgb(22.904968%,22.297668%,22.776794%);stop-opacity:1;" | |||
id="stop7070" /> | |||
<stop | |||
offset="0.8125" | |||
style="stop-color:rgb(22.703552%,22.084045%,22.557068%);stop-opacity:1;" | |||
id="stop7072" /> | |||
<stop | |||
offset="0.828125" | |||
style="stop-color:rgb(22.502136%,21.868896%,22.337341%);stop-opacity:1;" | |||
id="stop7074" /> | |||
<stop | |||
offset="0.84375" | |||
style="stop-color:rgb(22.30072%,21.655273%,22.117615%);stop-opacity:1;" | |||
id="stop7076" /> | |||
<stop | |||
offset="0.859375" | |||
style="stop-color:rgb(22.099304%,21.44165%,21.897888%);stop-opacity:1;" | |||
id="stop7078" /> | |||
<stop | |||
offset="0.875" | |||
style="stop-color:rgb(21.896362%,21.228027%,21.676636%);stop-opacity:1;" | |||
id="stop7080" /> | |||
<stop | |||
offset="0.890625" | |||
style="stop-color:rgb(21.694946%,21.014404%,21.456909%);stop-opacity:1;" | |||
id="stop7082" /> | |||
<stop | |||
offset="0.90625" | |||
style="stop-color:rgb(21.49353%,20.799255%,21.237183%);stop-opacity:1;" | |||
id="stop7084" /> | |||
<stop | |||
offset="0.921875" | |||
style="stop-color:rgb(21.292114%,20.585632%,21.017456%);stop-opacity:1;" | |||
id="stop7086" /> | |||
<stop | |||
offset="0.9375" | |||
style="stop-color:rgb(21.090698%,20.372009%,20.797729%);stop-opacity:1;" | |||
id="stop7088" /> | |||
<stop | |||
offset="0.953125" | |||
style="stop-color:rgb(20.889282%,20.158386%,20.576477%);stop-opacity:1;" | |||
id="stop7090" /> | |||
<stop | |||
offset="0.96875" | |||
style="stop-color:rgb(20.68634%,19.944763%,20.35675%);stop-opacity:1;" | |||
id="stop7092" /> | |||
<stop | |||
offset="0.984375" | |||
style="stop-color:rgb(20.484924%,19.729614%,20.137024%);stop-opacity:1;" | |||
id="stop7094" /> | |||
<stop | |||
offset="1" | |||
style="stop-color:rgb(20.283508%,19.515991%,19.917297%);stop-opacity:1;" | |||
id="stop7096" /> | |||
</linearGradient> | |||
<linearGradient | |||
inkscape:collect="always" | |||
xlink:href="#linearGradient1258" | |||
id="linearGradient1260" | |||
x1="97.483007" | |||
y1="102.72024" | |||
x2="112.67175" | |||
y2="102.72024" | |||
gradientUnits="userSpaceOnUse" | |||
gradientTransform="rotate(90,105.07738,102.72024)" /> | |||
</defs> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="5.6" | |||
inkscape:cx="27.755372" | |||
inkscape:cy="6.7266123" | |||
inkscape:document-units="mm" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
fit-margin-top="0" | |||
fit-margin-left="0" | |||
fit-margin-right="0" | |||
fit-margin-bottom="0" | |||
inkscape:window-width="1600" | |||
inkscape:window-height="900" | |||
inkscape:window-x="0" | |||
inkscape:window-y="0" | |||
inkscape:window-maximized="0" /> | |||
<metadata | |||
id="metadata8101"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title /> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(-96.577641,-94.219811)"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10029" | |||
d="m 96.577641,102.72091 c 0,4.6936 3.804779,8.49976 8.499759,8.49976 4.69498,0 8.49972,-3.80616 8.49972,-8.49976 0,-4.69498 -3.80474,-8.501099 -8.49972,-8.501099 -4.69498,0 -8.499759,3.806119 -8.499759,8.501099" | |||
style="fill:#050707;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path6967" | |||
d="m 105.07671,95.125869 c -4.19336,0 -7.594391,3.399651 -7.594391,7.594391 0,4.19473 3.401031,7.59435 7.594391,7.59435 4.19474,0 7.59573,-3.39962 7.59573,-7.59435 0,-4.19474 -3.40099,-7.594391 -7.59573,-7.594391" | |||
style="fill:url(#linearGradient1260);fill-opacity:1;stroke-width:0.35277778" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10037" | |||
d="m 106.17016,98.225744 c 0,0.657331 -0.53192,1.189249 -1.18925,1.189249 -0.65592,0 -1.18921,-0.531918 -1.18921,-1.189249 0,-0.657331 0.53329,-1.189249 1.18921,-1.189249 0.65733,0 1.18925,0.531918 1.18925,1.189249" | |||
style="fill:#050707;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10039" | |||
d="m 102.83394,99.691994 c 0,0.657326 -0.53191,1.189246 -1.18925,1.189246 -0.65595,0 -1.18924,-0.53192 -1.18924,-1.189246 0,-0.655955 0.53329,-1.189249 1.18924,-1.189249 0.65734,0 1.18925,0.533294 1.18925,1.189249" | |||
style="fill:#050707;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10041" | |||
d="m 109.33416,99.691994 c 0,0.657326 -0.53192,1.189246 -1.18925,1.189246 -0.65733,0 -1.18925,-0.53192 -1.18925,-1.189246 0,-0.655955 0.53192,-1.189249 1.18925,-1.189249 0.65733,0 1.18925,0.533294 1.18925,1.189249" | |||
style="fill:#050707;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10043" | |||
d="m 110.54133,102.72641 c 0,0.65596 -0.53192,1.18925 -1.18925,1.18925 -0.65596,0 -1.18925,-0.53329 -1.18925,-1.18925 0,-0.65729 0.53329,-1.18925 1.18925,-1.18925 0.65733,0 1.18925,0.53196 1.18925,1.18925" | |||
style="fill:#050707;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10045" | |||
d="m 101.79903,102.72641 c 0,0.65596 -0.53191,1.18925 -1.18924,1.18925 -0.655959,0 -1.189254,-0.53329 -1.189254,-1.18925 0,-0.65729 0.533295,-1.18925 1.189254,-1.18925 0.65733,0 1.18924,0.53196 1.18924,1.18925" | |||
style="fill:#050707;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10047" | |||
d="m 106.54778,109.2473 h -2.94076 v 1.28294 h 2.94076 z m 0,0" | |||
style="fill:#050707;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,376 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |||
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |||
<svg | |||
xmlns:dc="http://purl.org/dc/elements/1.1/" | |||
xmlns:cc="http://creativecommons.org/ns#" | |||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |||
xmlns:svg="http://www.w3.org/2000/svg" | |||
xmlns="http://www.w3.org/2000/svg" | |||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |||
width="10.90027mm" | |||
height="12.199803mm" | |||
viewBox="0 0 10.90027 12.199803" | |||
version="1.1" | |||
id="svg8104" | |||
inkscape:version="0.92.2 5c3e80d, 2017-08-06" | |||
sodipodi:docname="USB-B.svg"> | |||
<defs | |||
id="defs8098"> | |||
<clipPath | |||
id="clip306"> | |||
<path | |||
d="m 1550,2365 h 44 v 44 h -44 z m 0,0" | |||
id="path6964" | |||
inkscape:connector-curvature="0" /> | |||
</clipPath> | |||
<clipPath | |||
id="clip307"> | |||
<path | |||
d="m 1550.0859,2386.6914 c 0,11.8867 9.6368,21.5274 21.5274,21.5274 11.8906,0 21.5273,-9.6407 21.5273,-21.5274 0,-11.8906 -9.6367,-21.5312 -21.5273,-21.5312 -11.8906,0 -21.5274,9.6406 -21.5274,21.5312" | |||
id="path6967" | |||
inkscape:connector-curvature="0" /> | |||
</clipPath> | |||
<linearGradient | |||
id="linear3" | |||
gradientUnits="userSpaceOnUse" | |||
x1="-0.0115906" | |||
y1="0" | |||
x2="0.985816" | |||
y2="0" | |||
gradientTransform="matrix(0,43.168976,-43.168976,0,1571.6138,2365.6624)"> | |||
<stop | |||
offset="0" | |||
style="stop-color:rgb(32.940674%,32.940674%,33.724976%);stop-opacity:1;" | |||
id="stop6970" /> | |||
<stop | |||
offset="0.03125" | |||
style="stop-color:rgb(32.814026%,32.806396%,33.586121%);stop-opacity:1;" | |||
id="stop6972" /> | |||
<stop | |||
offset="0.046875" | |||
style="stop-color:rgb(32.58667%,32.565308%,33.338928%);stop-opacity:1;" | |||
id="stop6974" /> | |||
<stop | |||
offset="0.0625" | |||
style="stop-color:rgb(32.383728%,32.350159%,33.117676%);stop-opacity:1;" | |||
id="stop6976" /> | |||
<stop | |||
offset="0.078125" | |||
style="stop-color:rgb(32.182312%,32.136536%,32.897949%);stop-opacity:1;" | |||
id="stop6978" /> | |||
<stop | |||
offset="0.09375" | |||
style="stop-color:rgb(31.980896%,31.922913%,32.678223%);stop-opacity:1;" | |||
id="stop6980" /> | |||
<stop | |||
offset="0.109375" | |||
style="stop-color:rgb(31.77948%,31.70929%,32.458496%);stop-opacity:1;" | |||
id="stop6982" /> | |||
<stop | |||
offset="0.125" | |||
style="stop-color:rgb(31.578064%,31.495667%,32.23877%);stop-opacity:1;" | |||
id="stop6984" /> | |||
<stop | |||
offset="0.140625" | |||
style="stop-color:rgb(31.376648%,31.280518%,32.017517%);stop-opacity:1;" | |||
id="stop6986" /> | |||
<stop | |||
offset="0.15625" | |||
style="stop-color:rgb(31.173706%,31.066895%,31.797791%);stop-opacity:1;" | |||
id="stop6988" /> | |||
<stop | |||
offset="0.171875" | |||
style="stop-color:rgb(30.97229%,30.853271%,31.578064%);stop-opacity:1;" | |||
id="stop6990" /> | |||
<stop | |||
offset="0.1875" | |||
style="stop-color:rgb(30.770874%,30.639648%,31.358337%);stop-opacity:1;" | |||
id="stop6992" /> | |||
<stop | |||
offset="0.203125" | |||
style="stop-color:rgb(30.569458%,30.426025%,31.138611%);stop-opacity:1;" | |||
id="stop6994" /> | |||
<stop | |||
offset="0.21875" | |||
style="stop-color:rgb(30.368042%,30.212402%,30.917358%);stop-opacity:1;" | |||
id="stop6996" /> | |||
<stop | |||
offset="0.234375" | |||
style="stop-color:rgb(30.166626%,29.997253%,30.697632%);stop-opacity:1;" | |||
id="stop6998" /> | |||
<stop | |||
offset="0.25" | |||
style="stop-color:rgb(29.963684%,29.78363%,30.477905%);stop-opacity:1;" | |||
id="stop7000" /> | |||
<stop | |||
offset="0.265625" | |||
style="stop-color:rgb(29.762268%,29.570007%,30.258179%);stop-opacity:1;" | |||
id="stop7002" /> | |||
<stop | |||
offset="0.28125" | |||
style="stop-color:rgb(29.560852%,29.356384%,30.038452%);stop-opacity:1;" | |||
id="stop7004" /> | |||
<stop | |||
offset="0.296875" | |||
style="stop-color:rgb(29.359436%,29.142761%,29.8172%);stop-opacity:1;" | |||
id="stop7006" /> | |||
<stop | |||
offset="0.3125" | |||
style="stop-color:rgb(29.15802%,28.927612%,29.597473%);stop-opacity:1;" | |||
id="stop7008" /> | |||
<stop | |||
offset="0.328125" | |||
style="stop-color:rgb(28.955078%,28.713989%,29.377747%);stop-opacity:1;" | |||
id="stop7010" /> | |||
<stop | |||
offset="0.34375" | |||
style="stop-color:rgb(28.753662%,28.500366%,29.15802%);stop-opacity:1;" | |||
id="stop7012" /> | |||
<stop | |||
offset="0.359375" | |||
style="stop-color:rgb(28.552246%,28.286743%,28.938293%);stop-opacity:1;" | |||
id="stop7014" /> | |||
<stop | |||
offset="0.375" | |||
style="stop-color:rgb(28.35083%,28.07312%,28.718567%);stop-opacity:1;" | |||
id="stop7016" /> | |||
<stop | |||
offset="0.390625" | |||
style="stop-color:rgb(28.149414%,27.857971%,28.497314%);stop-opacity:1;" | |||
id="stop7018" /> | |||
<stop | |||
offset="0.40625" | |||
style="stop-color:rgb(27.947998%,27.644348%,28.277588%);stop-opacity:1;" | |||
id="stop7020" /> | |||
<stop | |||
offset="0.421875" | |||
style="stop-color:rgb(27.745056%,27.430725%,28.057861%);stop-opacity:1;" | |||
id="stop7022" /> | |||
<stop | |||
offset="0.4375" | |||
style="stop-color:rgb(27.54364%,27.217102%,27.838135%);stop-opacity:1;" | |||
id="stop7024" /> | |||
<stop | |||
offset="0.453125" | |||
style="stop-color:rgb(27.342224%,27.003479%,27.618408%);stop-opacity:1;" | |||
id="stop7026" /> | |||
<stop | |||
offset="0.46875" | |||
style="stop-color:rgb(27.140808%,26.789856%,27.397156%);stop-opacity:1;" | |||
id="stop7028" /> | |||
<stop | |||
offset="0.484375" | |||
style="stop-color:rgb(26.939392%,26.574707%,27.177429%);stop-opacity:1;" | |||
id="stop7030" /> | |||
<stop | |||
offset="0.5" | |||
style="stop-color:rgb(26.737976%,26.361084%,26.957703%);stop-opacity:1;" | |||
id="stop7032" /> | |||
<stop | |||
offset="0.515625" | |||
style="stop-color:rgb(26.535034%,26.147461%,26.737976%);stop-opacity:1;" | |||
id="stop7034" /> | |||
<stop | |||
offset="0.53125" | |||
style="stop-color:rgb(26.333618%,25.933838%,26.51825%);stop-opacity:1;" | |||
id="stop7036" /> | |||
<stop | |||
offset="0.546875" | |||
style="stop-color:rgb(26.132202%,25.720215%,26.296997%);stop-opacity:1;" | |||
id="stop7038" /> | |||
<stop | |||
offset="0.5625" | |||
style="stop-color:rgb(25.930786%,25.505066%,26.077271%);stop-opacity:1;" | |||
id="stop7040" /> | |||
<stop | |||
offset="0.578125" | |||
style="stop-color:rgb(25.72937%,25.291443%,25.857544%);stop-opacity:1;" | |||
id="stop7042" /> | |||
<stop | |||
offset="0.59375" | |||
style="stop-color:rgb(25.527954%,25.07782%,25.637817%);stop-opacity:1;" | |||
id="stop7044" /> | |||
<stop | |||
offset="0.609375" | |||
style="stop-color:rgb(25.325012%,24.864197%,25.418091%);stop-opacity:1;" | |||
id="stop7046" /> | |||
<stop | |||
offset="0.625" | |||
style="stop-color:rgb(25.123596%,24.650574%,25.196838%);stop-opacity:1;" | |||
id="stop7048" /> | |||
<stop | |||
offset="0.640625" | |||
style="stop-color:rgb(24.92218%,24.436951%,24.977112%);stop-opacity:1;" | |||
id="stop7050" /> | |||
<stop | |||
offset="0.65625" | |||
style="stop-color:rgb(24.720764%,24.221802%,24.757385%);stop-opacity:1;" | |||
id="stop7052" /> | |||
<stop | |||
offset="0.671875" | |||
style="stop-color:rgb(24.519348%,24.008179%,24.537659%);stop-opacity:1;" | |||
id="stop7054" /> | |||
<stop | |||
offset="0.6875" | |||
style="stop-color:rgb(24.316406%,23.794556%,24.317932%);stop-opacity:1;" | |||
id="stop7056" /> | |||
<stop | |||
offset="0.703125" | |||
style="stop-color:rgb(24.11499%,23.580933%,24.098206%);stop-opacity:1;" | |||
id="stop7058" /> | |||
<stop | |||
offset="0.71875" | |||
style="stop-color:rgb(23.913574%,23.36731%,23.876953%);stop-opacity:1;" | |||
id="stop7060" /> | |||
<stop | |||
offset="0.734375" | |||
style="stop-color:rgb(23.712158%,23.152161%,23.657227%);stop-opacity:1;" | |||
id="stop7062" /> | |||
<stop | |||
offset="0.75" | |||
style="stop-color:rgb(23.510742%,22.938538%,23.4375%);stop-opacity:1;" | |||
id="stop7064" /> | |||
<stop | |||
offset="0.765625" | |||
style="stop-color:rgb(23.309326%,22.724915%,23.217773%);stop-opacity:1;" | |||
id="stop7066" /> | |||
<stop | |||
offset="0.78125" | |||
style="stop-color:rgb(23.106384%,22.511292%,22.998047%);stop-opacity:1;" | |||
id="stop7068" /> | |||
<stop | |||
offset="0.796875" | |||
style="stop-color:rgb(22.904968%,22.297668%,22.776794%);stop-opacity:1;" | |||
id="stop7070" /> | |||
<stop | |||
offset="0.8125" | |||
style="stop-color:rgb(22.703552%,22.084045%,22.557068%);stop-opacity:1;" | |||
id="stop7072" /> | |||
<stop | |||
offset="0.828125" | |||
style="stop-color:rgb(22.502136%,21.868896%,22.337341%);stop-opacity:1;" | |||
id="stop7074" /> | |||
<stop | |||
offset="0.84375" | |||
style="stop-color:rgb(22.30072%,21.655273%,22.117615%);stop-opacity:1;" | |||
id="stop7076" /> | |||
<stop | |||
offset="0.859375" | |||
style="stop-color:rgb(22.099304%,21.44165%,21.897888%);stop-opacity:1;" | |||
id="stop7078" /> | |||
<stop | |||
offset="0.875" | |||
style="stop-color:rgb(21.896362%,21.228027%,21.676636%);stop-opacity:1;" | |||
id="stop7080" /> | |||
<stop | |||
offset="0.890625" | |||
style="stop-color:rgb(21.694946%,21.014404%,21.456909%);stop-opacity:1;" | |||
id="stop7082" /> | |||
<stop | |||
offset="0.90625" | |||
style="stop-color:rgb(21.49353%,20.799255%,21.237183%);stop-opacity:1;" | |||
id="stop7084" /> | |||
<stop | |||
offset="0.921875" | |||
style="stop-color:rgb(21.292114%,20.585632%,21.017456%);stop-opacity:1;" | |||
id="stop7086" /> | |||
<stop | |||
offset="0.9375" | |||
style="stop-color:rgb(21.090698%,20.372009%,20.797729%);stop-opacity:1;" | |||
id="stop7088" /> | |||
<stop | |||
offset="0.953125" | |||
style="stop-color:rgb(20.889282%,20.158386%,20.576477%);stop-opacity:1;" | |||
id="stop7090" /> | |||
<stop | |||
offset="0.96875" | |||
style="stop-color:rgb(20.68634%,19.944763%,20.35675%);stop-opacity:1;" | |||
id="stop7092" /> | |||
<stop | |||
offset="0.984375" | |||
style="stop-color:rgb(20.484924%,19.729614%,20.137024%);stop-opacity:1;" | |||
id="stop7094" /> | |||
<stop | |||
offset="1" | |||
style="stop-color:rgb(20.283508%,19.515991%,19.917297%);stop-opacity:1;" | |||
id="stop7096" /> | |||
</linearGradient> | |||
</defs> | |||
<sodipodi:namedview | |||
id="base" | |||
pagecolor="#ffffff" | |||
bordercolor="#666666" | |||
borderopacity="1.0" | |||
inkscape:pageopacity="0.0" | |||
inkscape:pageshadow="2" | |||
inkscape:zoom="1.979899" | |||
inkscape:cx="-53.032455" | |||
inkscape:cy="-10.55258" | |||
inkscape:document-units="mm" | |||
inkscape:current-layer="layer1" | |||
showgrid="false" | |||
fit-margin-top="0" | |||
fit-margin-left="0" | |||
fit-margin-right="0" | |||
fit-margin-bottom="0" | |||
inkscape:window-width="1600" | |||
inkscape:window-height="882" | |||
inkscape:window-x="0" | |||
inkscape:window-y="18" | |||
inkscape:window-maximized="0" /> | |||
<metadata | |||
id="metadata8101"> | |||
<rdf:RDF> | |||
<cc:Work | |||
rdf:about=""> | |||
<dc:format>image/svg+xml</dc:format> | |||
<dc:type | |||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | |||
<dc:title></dc:title> | |||
</cc:Work> | |||
</rdf:RDF> | |||
</metadata> | |||
<g | |||
inkscape:label="Layer 1" | |||
inkscape:groupmode="layer" | |||
id="layer1" | |||
transform="translate(-113.84542,-95.231797)"> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10059" | |||
d="M 124.74569,107.4316 H 113.84542 V 95.231797 h 10.90027 z m 0,0" | |||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10061" | |||
d="m 115.94967,107.08157 c -1.04733,0 -1.90444,-0.85714 -1.90444,-1.90447 v -7.68943 c 0,-1.047327 0.85711,-1.905847 1.90444,-1.905847 h 6.69036 c 1.04733,0 1.90447,0.85852 1.90447,1.905847 v 7.68943 c 0,1.04733 -0.85714,1.90447 -1.90447,1.90447 z m 0,0" | |||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10063" | |||
d="m 123.47237,98.619028 c 0,-0.216358 -0.11712,-0.516749 -0.2577,-0.668337 l -1.16304,-1.233347 c -0.14196,-0.151588 -0.42446,-0.277001 -0.62703,-0.277001 h -5.57138 c -0.40655,0 -0.73727,0.352778 -0.73727,0.784119 v 8.215848 c 0,0.42996 0.33072,0.78412 0.73727,0.78412 h 5.57138 c 0.20257,0 0.48507,-0.12679 0.62841,-0.27838 l 1.16166,-1.23197 c 0.14058,-0.15155 0.2577,-0.45336 0.2577,-0.66971 z m 0,0" | |||
style="fill:#cccaca;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10065" | |||
d="m 121.75946,104.99383 h -4.69357 v -7.164424 h 4.69357 z m 0,0" | |||
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#a1a3a1;stroke-width:0.08819444;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3;stroke-opacity:1" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10067" | |||
d="m 120.60193,103.78529 h -2.53284 v -4.745963 h 2.53284 z m 0,0" | |||
style="fill:#424242;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10069" | |||
d="m 120.84309,99.71458 h -0.37483 v 1.10243 h 0.37483 z m 0,0" | |||
style="fill:#e1ba21;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
<path | |||
inkscape:connector-curvature="0" | |||
id="path10071" | |||
d="m 120.84309,102.00764 h -0.37483 v 1.10243 h 0.37483 z m 0,0" | |||
style="fill:#e1ba21;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.35277775" /> | |||
</g> | |||
</svg> |
@@ -0,0 +1,115 @@ | |||
#include "app.hpp" | |||
#include "audio.hpp" | |||
namespace rack { | |||
struct AudioDriverItem : MenuItem { | |||
AudioIO *audioIO; | |||
int driver; | |||
void onAction(EventAction &e) override { | |||
audioIO->setDriver(driver); | |||
} | |||
}; | |||
struct AudioDeviceItem : MenuItem { | |||
AudioIO *audioIO; | |||
int device; | |||
void onAction(EventAction &e) override { | |||
audioIO->device = device; | |||
audioIO->openStream(); | |||
} | |||
}; | |||
struct AudioSampleRateItem : MenuItem { | |||
AudioIO *audioIO; | |||
int sampleRate; | |||
void onAction(EventAction &e) override { | |||
audioIO->sampleRate = sampleRate; | |||
audioIO->openStream(); | |||
} | |||
}; | |||
struct AudioBlockSizeItem : MenuItem { | |||
AudioIO *audioIO; | |||
int blockSize; | |||
void onAction(EventAction &e) override { | |||
audioIO->blockSize = blockSize; | |||
audioIO->openStream(); | |||
} | |||
}; | |||
void AudioWidget::onMouseDown(EventMouseDown &e) { | |||
OpaqueWidget::onMouseDown(e); | |||
if (!audioIO) | |||
return; | |||
Menu *menu = gScene->createMenu(); | |||
// Audio driver | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio driver")); | |||
for (int driver : audioIO->listDrivers()) { | |||
AudioDriverItem *item = new AudioDriverItem(); | |||
item->audioIO = audioIO; | |||
item->driver = driver; | |||
item->text = audioIO->getDriverName(driver); | |||
item->rightText = CHECKMARK(item->driver == audioIO->getDriver()); | |||
menu->addChild(item); | |||
} | |||
menu->addChild(construct<MenuEntry>()); | |||
// Audio device | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Audio device")); | |||
int deviceCount = audioIO->getDeviceCount(); | |||
{ | |||
AudioDeviceItem *item = new AudioDeviceItem(); | |||
item->audioIO = audioIO; | |||
item->device = -1; | |||
item->text = "No device"; | |||
item->rightText = CHECKMARK(item->device == audioIO->device); | |||
menu->addChild(item); | |||
} | |||
for (int device = 0; device < deviceCount; device++) { | |||
AudioDeviceItem *item = new AudioDeviceItem(); | |||
item->audioIO = audioIO; | |||
item->device = device; | |||
item->text = audioIO->getDeviceDetail(device); | |||
item->rightText = CHECKMARK(item->device == audioIO->device); | |||
menu->addChild(item); | |||
} | |||
menu->addChild(construct<MenuEntry>()); | |||
// Sample rate | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Sample rate")); | |||
for (int sampleRate : audioIO->listSampleRates()) { | |||
AudioSampleRateItem *item = new AudioSampleRateItem(); | |||
item->audioIO = audioIO; | |||
item->sampleRate = sampleRate; | |||
item->text = stringf("%d Hz", sampleRate); | |||
item->rightText = CHECKMARK(item->sampleRate == audioIO->sampleRate); | |||
menu->addChild(item); | |||
} | |||
menu->addChild(construct<MenuEntry>()); | |||
// Block size | |||
menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Block size")); | |||
std::vector<int> blockSizes = {64, 128, 256, 512, 1024, 2048, 4096}; | |||
for (int blockSize : blockSizes) { | |||
AudioBlockSizeItem *item = new AudioBlockSizeItem(); | |||
item->audioIO = audioIO; | |||
item->blockSize = blockSize; | |||
float latency = (float) blockSize / audioIO->sampleRate * 1000.0; | |||
item->text = stringf("%d (%.1f ms)", blockSize, latency); | |||
item->rightText = CHECKMARK(item->blockSize == audioIO->blockSize); | |||
menu->addChild(item); | |||
} | |||
} | |||
} // namespace rack |
@@ -1,7 +1,7 @@ | |||
#include "app.hpp" | |||
#include "gui.hpp" | |||
#include "components.hpp" | |||
#include "engine.hpp" | |||
#include "componentlibrary.hpp" | |||
namespace rack { | |||
@@ -53,13 +53,13 @@ struct FileChoice : ChoiceButton { | |||
}; | |||
struct PauseItem : MenuItem { | |||
struct EnginePauseItem : MenuItem { | |||
void onAction(EventAction &e) override { | |||
gPaused = !gPaused; | |||
} | |||
}; | |||
struct SampleRateItem : MenuItem { | |||
struct EngineSampleRateItem : MenuItem { | |||
float sampleRate; | |||
void onAction(EventAction &e) override { | |||
engineSetSampleRate(sampleRate); | |||
@@ -67,22 +67,21 @@ struct SampleRateItem : MenuItem { | |||
} | |||
}; | |||
struct SampleRateChoice : ChoiceButton { | |||
struct EngineSampleRateChoice : ChoiceButton { | |||
void onAction(EventAction &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)); | |||
menu->box.size.x = box.size.x; | |||
PauseItem *pauseItem = new PauseItem(); | |||
EnginePauseItem *pauseItem = new EnginePauseItem(); | |||
pauseItem->text = gPaused ? "Resume engine" : "Pause engine"; | |||
menu->addChild(pauseItem); | |||
float sampleRates[] = {44100, 48000, 88200, 96000, 176400, 192000}; | |||
int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]); | |||
for (int i = 0; i < sampleRatesLen; i++) { | |||
SampleRateItem *item = new SampleRateItem(); | |||
item->text = stringf("%.0f Hz", sampleRates[i]); | |||
item->sampleRate = sampleRates[i]; | |||
std::vector<float> sampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | |||
for (float sampleRate : sampleRates) { | |||
EngineSampleRateItem *item = new EngineSampleRateItem(); | |||
item->text = stringf("%.0f Hz", sampleRate); | |||
item->sampleRate = sampleRate; | |||
menu->addChild(item); | |||
} | |||
} | |||
@@ -112,7 +111,7 @@ Toolbar::Toolbar() { | |||
xPos += margin; | |||
{ | |||
SampleRateChoice *srChoice = new SampleRateChoice(); | |||
EngineSampleRateChoice *srChoice = new EngineSampleRateChoice(); | |||
srChoice->box.pos = Vec(xPos, margin); | |||
srChoice->box.size.x = 100; | |||
addChild(srChoice); | |||
@@ -1,6 +1,6 @@ | |||
#include "app.hpp" | |||
#include "engine.hpp" | |||
#include "components.hpp" | |||
#include "componentlibrary.hpp" | |||
#include "gui.hpp" | |||
@@ -0,0 +1,212 @@ | |||
#include "util.hpp" | |||
#include "math.hpp" | |||
#include "audio.hpp" | |||
namespace rack { | |||
AudioIO::AudioIO() { | |||
setDriver(RtAudio::UNSPECIFIED); | |||
} | |||
AudioIO::~AudioIO() { | |||
closeStream(); | |||
} | |||
std::vector<int> AudioIO::listDrivers() { | |||
std::vector<RtAudio::Api> apis; | |||
RtAudio::getCompiledApi(apis); | |||
std::vector<int> drivers; | |||
for (RtAudio::Api api : apis) | |||
drivers.push_back((int) api); | |||
return drivers; | |||
} | |||
std::string AudioIO::getDriverName(int driver) { | |||
switch (driver) { | |||
case RtAudio::UNSPECIFIED: return "Unspecified"; | |||
case RtAudio::LINUX_ALSA: return "ALSA"; | |||
case RtAudio::LINUX_PULSE: return "PulseAudio"; | |||
case RtAudio::LINUX_OSS: return "OSS"; | |||
case RtAudio::UNIX_JACK: return "JACK"; | |||
case RtAudio::MACOSX_CORE: return "Core Audio"; | |||
case RtAudio::WINDOWS_WASAPI: return "WASAPI"; | |||
case RtAudio::WINDOWS_ASIO: return "ASIO"; | |||
case RtAudio::WINDOWS_DS: return "DirectSound"; | |||
case RtAudio::RTAUDIO_DUMMY: return "Dummy"; | |||
default: return "Unknown"; | |||
} | |||
} | |||
int AudioIO::getDriver() { | |||
if (!stream) | |||
return RtAudio::UNSPECIFIED; | |||
return stream->getCurrentApi(); | |||
} | |||
void AudioIO::setDriver(int driver) { | |||
// closeStream(); | |||
if (stream) | |||
delete stream; | |||
stream = new RtAudio((RtAudio::Api) driver); | |||
} | |||
int AudioIO::getDeviceCount() { | |||
if (!stream) | |||
return 0; | |||
return stream->getDeviceCount(); | |||
} | |||
std::string AudioIO::getDeviceName(int device) { | |||
if (!stream || device < 0) | |||
return ""; | |||
try { | |||
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); | |||
return deviceInfo.name; | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
return ""; | |||
} | |||
} | |||
std::string AudioIO::getDeviceDetail(int device) { | |||
if (!stream || device < 0) | |||
return ""; | |||
try { | |||
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); | |||
return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
return ""; | |||
} | |||
} | |||
static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { | |||
AudioIO *audioIO = (AudioIO*) userData; | |||
assert(audioIO); | |||
// audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); | |||
return 0; | |||
} | |||
void AudioIO::openStream() { | |||
// Close device but remember the current device number | |||
int device = this->device; | |||
closeStream(); | |||
if (!stream) | |||
return; | |||
// Open new device | |||
if (device >= 0) { | |||
RtAudio::DeviceInfo deviceInfo; | |||
try { | |||
deviceInfo = stream->getDeviceInfo(device); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
return; | |||
} | |||
numOutputs = mini(deviceInfo.outputChannels, maxOutputs); | |||
numInputs = mini(deviceInfo.inputChannels, maxInputs); | |||
if (numOutputs == 0 && numInputs == 0) { | |||
warn("Audio device %d has 0 inputs and 0 outputs"); | |||
return; | |||
} | |||
RtAudio::StreamParameters outParameters; | |||
outParameters.deviceId = device; | |||
outParameters.nChannels = numOutputs; | |||
RtAudio::StreamParameters inParameters; | |||
inParameters.deviceId = device; | |||
inParameters.nChannels = numInputs; | |||
RtAudio::StreamOptions options; | |||
// options.flags |= RTAUDIO_SCHEDULE_REALTIME; | |||
// Find closest sample rate | |||
unsigned int closestSampleRate = 0; | |||
for (unsigned int sr : deviceInfo.sampleRates) { | |||
if (fabsf(sr - sampleRate) < fabsf(closestSampleRate - sampleRate)) { | |||
closestSampleRate = sr; | |||
} | |||
} | |||
try { | |||
debug("Opening audio stream %d", device); | |||
stream->openStream( | |||
numOutputs == 0 ? NULL : &outParameters, | |||
numInputs == 0 ? NULL : &inParameters, | |||
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to open audio stream: %s", e.what()); | |||
return; | |||
} | |||
try { | |||
debug("Starting audio stream %d", device); | |||
stream->startStream(); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to start audio stream: %s", e.what()); | |||
return; | |||
} | |||
// Update sample rate because this may have changed | |||
this->sampleRate = stream->getStreamSampleRate(); | |||
this->device = device; | |||
} | |||
} | |||
void AudioIO::closeStream() { | |||
if (stream) { | |||
if (stream->isStreamRunning()) { | |||
debug("Aborting audio stream %d", device); | |||
try { | |||
stream->abortStream(); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to abort stream %s", e.what()); | |||
} | |||
} | |||
if (stream->isStreamOpen()) { | |||
debug("Closing audio stream %d", device); | |||
try { | |||
stream->closeStream(); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to close stream %s", e.what()); | |||
} | |||
} | |||
} | |||
// Reset stream settings | |||
device = -1; | |||
numOutputs = 0; | |||
numInputs = 0; | |||
} | |||
std::vector<int> AudioIO::listSampleRates() { | |||
if (!stream || device < 0) | |||
return {}; | |||
try { | |||
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); | |||
std::vector<int> sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); | |||
return sampleRates; | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
return {}; | |||
} | |||
} | |||
} // namespace rack |
@@ -1,8 +1,8 @@ | |||
#include <assert.h> | |||
#include <mutex> | |||
#include <thread> | |||
#include <algorithm> | |||
#include "core.hpp" | |||
#include "audio.hpp" | |||
#include "dsp/samplerate.hpp" | |||
#include "dsp/ringbuffer.hpp" | |||
@@ -29,13 +29,7 @@ struct AudioInterface : Module { | |||
NUM_OUTPUTS = AUDIO1_OUTPUT + 8 | |||
}; | |||
RtAudio *stream = NULL; | |||
// Stream properties | |||
int device = -1; | |||
float sampleRate = 44100.0; | |||
int blockSize = 256; | |||
int numOutputs = 0; | |||
int numInputs = 0; | |||
AudioIO audioIO; | |||
SampleRateConverter<8> inputSrc; | |||
SampleRateConverter<8> outputSrc; | |||
@@ -47,89 +41,43 @@ struct AudioInterface : Module { | |||
DoubleRingBuffer<Frame<8>, (1<<15)> inputSrcBuffer; | |||
AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | |||
setDriver(RtAudio::UNSPECIFIED); | |||
} | |||
~AudioInterface() { | |||
closeStream(); | |||
} | |||
void step() override; | |||
void stepStream(const float *input, float *output, int numFrames); | |||
int getDeviceCount(); | |||
std::string getDeviceName(int device); | |||
void openStream(); | |||
void closeStream(); | |||
void setDriver(int driver) { | |||
closeStream(); | |||
if (stream) | |||
delete stream; | |||
stream = new RtAudio((RtAudio::Api) driver); | |||
} | |||
int getDriver() { | |||
if (!stream) | |||
return RtAudio::UNSPECIFIED; | |||
return stream->getCurrentApi(); | |||
} | |||
std::vector<int> getAvailableDrivers() { | |||
std::vector<RtAudio::Api> apis; | |||
RtAudio::getCompiledApi(apis); | |||
std::vector<int> drivers; | |||
for (RtAudio::Api api : apis) | |||
drivers.push_back(api); | |||
return drivers; | |||
} | |||
std::string getDriverName(int driver) { | |||
switch (driver) { | |||
case RtAudio::UNSPECIFIED: return "Unspecified"; | |||
case RtAudio::LINUX_ALSA: return "ALSA"; | |||
case RtAudio::LINUX_PULSE: return "PulseAudio"; | |||
case RtAudio::LINUX_OSS: return "OSS"; | |||
case RtAudio::UNIX_JACK: return "JACK"; | |||
case RtAudio::MACOSX_CORE: return "Core Audio"; | |||
case RtAudio::WINDOWS_WASAPI: return "WASAPI"; | |||
case RtAudio::WINDOWS_ASIO: return "ASIO"; | |||
case RtAudio::WINDOWS_DS: return "DirectSound"; | |||
case RtAudio::RTAUDIO_DUMMY: return "Dummy"; | |||
default: return "Unknown"; | |||
} | |||
} | |||
std::vector<float> getSampleRates(); | |||
json_t *toJson() override { | |||
json_t *rootJ = json_object(); | |||
json_object_set_new(rootJ, "driver", json_integer(getDriver())); | |||
json_object_set_new(rootJ, "device", json_integer(device)); | |||
json_object_set_new(rootJ, "sampleRate", json_real(sampleRate)); | |||
json_object_set_new(rootJ, "blockSize", json_integer(blockSize)); | |||
// json_object_set_new(rootJ, "driver", json_integer(getDriver())); | |||
// json_object_set_new(rootJ, "device", json_integer(device)); | |||
// json_object_set_new(rootJ, "audioIO.sampleRate", json_real(audioIO.sampleRate)); | |||
// json_object_set_new(rootJ, "audioIO.blockSize", json_integer(audioIO.blockSize)); | |||
return rootJ; | |||
} | |||
void fromJson(json_t *rootJ) override { | |||
json_t *driverJ = json_object_get(rootJ, "driver"); | |||
if (driverJ) | |||
setDriver(json_number_value(driverJ)); | |||
// json_t *driverJ = json_object_get(rootJ, "driver"); | |||
// if (driverJ) | |||
// setDriver(json_number_value(driverJ)); | |||
json_t *deviceJ = json_object_get(rootJ, "device"); | |||
if (deviceJ) | |||
device = json_number_value(deviceJ); | |||
// json_t *deviceJ = json_object_get(rootJ, "device"); | |||
// if (deviceJ) | |||
// device = json_number_value(deviceJ); | |||
json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); | |||
if (sampleRateJ) | |||
sampleRate = json_number_value(sampleRateJ); | |||
// json_t *sampleRateJ = json_object_get(rootJ, "audioIO.sampleRate"); | |||
// if (sampleRateJ) | |||
// audioIO.sampleRate = json_number_value(sampleRateJ); | |||
json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); | |||
if (blockSizeJ) | |||
blockSize = json_integer_value(blockSizeJ); | |||
// json_t *blockSizeJ = json_object_get(rootJ, "audioIO.blockSize"); | |||
// if (blockSizeJ) | |||
// audioIO.blockSize = json_integer_value(blockSizeJ); | |||
openStream(); | |||
// openStream(); | |||
} | |||
void onReset() override { | |||
closeStream(); | |||
audioIO.closeStream(); | |||
} | |||
}; | |||
@@ -149,15 +97,15 @@ struct AudioInterface : Module { | |||
void AudioInterface::step() { | |||
// debug("inputBuffer %d inputSrcBuffer %d outputBuffer %d", inputBuffer.size(), inputSrcBuffer.size(), outputBuffer.size()); | |||
// Read/write stream if we have enough input, OR the output buffer is empty if we have no input | |||
if (numOutputs > 0) { | |||
TIMED_SLEEP_LOCK(inputSrcBuffer.size() < blockSize, 100e-6, 0.2); | |||
if (audioIO.numOutputs > 0) { | |||
TIMED_SLEEP_LOCK(inputSrcBuffer.size() < audioIO.blockSize, 100e-6, 0.2); | |||
} | |||
else if (numInputs > 0) { | |||
else if (audioIO.numInputs > 0) { | |||
TIMED_SLEEP_LOCK(!outputBuffer.empty(), 100e-6, 0.2); | |||
} | |||
// Get input and pass it through the sample rate converter | |||
if (numOutputs > 0) { | |||
if (audioIO.numOutputs > 0) { | |||
if (!inputBuffer.full()) { | |||
Frame<8> f; | |||
for (int i = 0; i < 8; i++) { | |||
@@ -169,7 +117,7 @@ void AudioInterface::step() { | |||
// Once full, sample rate convert the input | |||
// inputBuffer -> SRC -> inputSrcBuffer | |||
if (inputBuffer.full()) { | |||
inputSrc.setRates(engineGetSampleRate(), sampleRate); | |||
inputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); | |||
int inLen = inputBuffer.size(); | |||
int outLen = inputSrcBuffer.capacity(); | |||
inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen); | |||
@@ -189,29 +137,29 @@ void AudioInterface::step() { | |||
void AudioInterface::stepStream(const float *input, float *output, int numFrames) { | |||
if (gPaused) { | |||
memset(output, 0, sizeof(float) * numOutputs * numFrames); | |||
memset(output, 0, sizeof(float) * audioIO.numOutputs * numFrames); | |||
return; | |||
} | |||
if (numOutputs > 0) { | |||
if (audioIO.numOutputs > 0) { | |||
// Wait for enough input before proceeding | |||
TIMED_SLEEP_LOCK(inputSrcBuffer.size() >= numFrames, 100e-6, 0.2); | |||
} | |||
else if (numInputs > 0) { | |||
else if (audioIO.numInputs > 0) { | |||
TIMED_SLEEP_LOCK(outputBuffer.empty(), 100e-6, 0.2); | |||
} | |||
// input stream -> output buffer | |||
if (numInputs > 0) { | |||
if (audioIO.numInputs > 0) { | |||
Frame<8> inputFrames[numFrames]; | |||
for (int i = 0; i < numFrames; i++) { | |||
for (int c = 0; c < 8; c++) { | |||
inputFrames[i].samples[c] = (c < numInputs) ? input[i*numInputs + c] : 0.0; | |||
inputFrames[i].samples[c] = (c < audioIO.numInputs) ? input[i*audioIO.numInputs + c] : 0.0; | |||
} | |||
} | |||
// Pass output through sample rate converter | |||
outputSrc.setRates(sampleRate, engineGetSampleRate()); | |||
outputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); | |||
int inLen = numFrames; | |||
int outLen = outputBuffer.capacity(); | |||
outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); | |||
@@ -219,7 +167,7 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames | |||
} | |||
// input buffer -> output stream | |||
if (numOutputs > 0) { | |||
if (audioIO.numOutputs > 0) { | |||
for (int i = 0; i < numFrames; i++) { | |||
Frame<8> f; | |||
if (inputSrcBuffer.empty()) { | |||
@@ -228,304 +176,23 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames | |||
else { | |||
f = inputSrcBuffer.shift(); | |||
} | |||
for (int c = 0; c < numOutputs; c++) { | |||
output[i*numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); | |||
for (int c = 0; c < audioIO.numOutputs; c++) { | |||
output[i*audioIO.numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); | |||
} | |||
} | |||
} | |||
} | |||
int AudioInterface::getDeviceCount() { | |||
if (!stream) | |||
return 0; | |||
return stream->getDeviceCount(); | |||
} | |||
std::string AudioInterface::getDeviceName(int device) { | |||
if (!stream || device < 0) | |||
return ""; | |||
try { | |||
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); | |||
return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
return ""; | |||
} | |||
} | |||
static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) { | |||
AudioInterface *audioInterface = (AudioInterface *) userData; | |||
assert(audioInterface); | |||
audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames); | |||
return 0; | |||
} | |||
void AudioInterface::openStream() { | |||
int device = this->device; | |||
closeStream(); | |||
if (!stream) | |||
return; | |||
// Open new device | |||
if (device >= 0) { | |||
RtAudio::DeviceInfo deviceInfo; | |||
try { | |||
deviceInfo = stream->getDeviceInfo(device); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
return; | |||
} | |||
numOutputs = mini(deviceInfo.outputChannels, 8); | |||
numInputs = mini(deviceInfo.inputChannels, 8); | |||
if (numOutputs == 0 && numInputs == 0) { | |||
warn("Audio device %d has 0 inputs and 0 outputs"); | |||
return; | |||
} | |||
RtAudio::StreamParameters outParameters; | |||
outParameters.deviceId = device; | |||
outParameters.nChannels = numOutputs; | |||
RtAudio::StreamParameters inParameters; | |||
inParameters.deviceId = device; | |||
inParameters.nChannels = numInputs; | |||
RtAudio::StreamOptions options; | |||
// options.flags |= RTAUDIO_SCHEDULE_REALTIME; | |||
// Find closest sample rate | |||
unsigned int closestSampleRate = 0; | |||
for (unsigned int sr : deviceInfo.sampleRates) { | |||
if (fabsf(sr - sampleRate) < fabsf(closestSampleRate - sampleRate)) { | |||
closestSampleRate = sr; | |||
} | |||
} | |||
try { | |||
// Don't use stream parameters if 0 input or output channels | |||
debug("Opening audio stream %d", device); | |||
stream->openStream( | |||
numOutputs == 0 ? NULL : &outParameters, | |||
numInputs == 0 ? NULL : &inParameters, | |||
RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to open audio stream: %s", e.what()); | |||
return; | |||
} | |||
try { | |||
debug("Starting audio stream %d", device); | |||
stream->startStream(); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to start audio stream: %s", e.what()); | |||
return; | |||
} | |||
// Update sample rate because this may have changed | |||
this->sampleRate = stream->getStreamSampleRate(); | |||
this->device = device; | |||
} | |||
} | |||
void AudioInterface::closeStream() { | |||
if (stream) { | |||
if (stream->isStreamRunning()) { | |||
debug("Aborting audio stream %d", device); | |||
try { | |||
stream->abortStream(); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to abort stream %s", e.what()); | |||
} | |||
} | |||
if (stream->isStreamOpen()) { | |||
debug("Closing audio stream %d", device); | |||
try { | |||
stream->closeStream(); | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to close stream %s", e.what()); | |||
} | |||
} | |||
} | |||
// Reset stream settings | |||
device = -1; | |||
numOutputs = 0; | |||
numInputs = 0; | |||
// Clear buffers | |||
inputBuffer.clear(); | |||
outputBuffer.clear(); | |||
inputSrcBuffer.clear(); | |||
inputSrc.reset(); | |||
outputSrc.reset(); | |||
} | |||
std::vector<float> AudioInterface::getSampleRates() { | |||
std::vector<float> allowedSampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | |||
if (!stream || device < 0) | |||
return allowedSampleRates; | |||
try { | |||
std::vector<float> sampleRates; | |||
RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(device); | |||
for (int sr : deviceInfo.sampleRates) { | |||
float sampleRate = sr; | |||
auto allowedIt = std::find(allowedSampleRates.begin(), allowedSampleRates.end(), sampleRate); | |||
if (allowedIt != allowedSampleRates.end()) { | |||
sampleRates.push_back(sampleRate); | |||
} | |||
} | |||
return sampleRates; | |||
} | |||
catch (RtAudioError &e) { | |||
warn("Failed to query audio device: %s", e.what()); | |||
return {}; | |||
} | |||
} | |||
struct AudioDriverItem : MenuItem { | |||
AudioInterface *audioInterface; | |||
int driver; | |||
void onAction(EventAction &e) override { | |||
audioInterface->setDriver(driver); | |||
} | |||
}; | |||
struct AudioDriverChoice : ChoiceButton { | |||
AudioInterface *audioInterface; | |||
void onAction(EventAction &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||
menu->box.size.x = box.size.x; | |||
for (int driver : audioInterface->getAvailableDrivers()) { | |||
AudioDriverItem *audioItem = new AudioDriverItem(); | |||
audioItem->audioInterface = audioInterface; | |||
audioItem->driver = driver; | |||
audioItem->text = audioInterface->getDriverName(driver); | |||
menu->addChild(audioItem); | |||
} | |||
} | |||
void step() override { | |||
text = audioInterface->getDriverName(audioInterface->getDriver()); | |||
} | |||
}; | |||
struct AudioDeviceItem : MenuItem { | |||
AudioInterface *audioInterface; | |||
int device; | |||
void onAction(EventAction &e) override { | |||
audioInterface->device = device; | |||
audioInterface->openStream(); | |||
} | |||
}; | |||
struct AudioDeviceChoice : ChoiceButton { | |||
int lastDeviceId = -1; | |||
AudioInterface *audioInterface; | |||
void onAction(EventAction &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||
menu->box.size.x = box.size.x; | |||
int deviceCount = audioInterface->getDeviceCount(); | |||
{ | |||
AudioDeviceItem *audioItem = new AudioDeviceItem(); | |||
audioItem->audioInterface = audioInterface; | |||
audioItem->device = -1; | |||
audioItem->text = "No device"; | |||
menu->addChild(audioItem); | |||
} | |||
for (int device = 0; device < deviceCount; device++) { | |||
AudioDeviceItem *audioItem = new AudioDeviceItem(); | |||
audioItem->audioInterface = audioInterface; | |||
audioItem->device = device; | |||
audioItem->text = audioInterface->getDeviceName(device); | |||
menu->addChild(audioItem); | |||
} | |||
} | |||
void step() override { | |||
if (lastDeviceId != audioInterface->device) { | |||
std::string name = audioInterface->getDeviceName(audioInterface->device); | |||
text = ellipsize(name, 24); | |||
lastDeviceId = audioInterface->device; | |||
} | |||
} | |||
}; | |||
struct SampleRateItem : MenuItem { | |||
AudioInterface *audioInterface; | |||
float sampleRate; | |||
void onAction(EventAction &e) override { | |||
audioInterface->sampleRate = sampleRate; | |||
audioInterface->openStream(); | |||
} | |||
}; | |||
struct SampleRateChoice : ChoiceButton { | |||
AudioInterface *audioInterface; | |||
void onAction(EventAction &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||
menu->box.size.x = box.size.x; | |||
for (float sampleRate : audioInterface->getSampleRates()) { | |||
SampleRateItem *item = new SampleRateItem(); | |||
item->audioInterface = audioInterface; | |||
item->sampleRate = sampleRate; | |||
item->text = stringf("%.0f Hz", sampleRate); | |||
menu->addChild(item); | |||
} | |||
} | |||
void step() override { | |||
this->text = stringf("%.0f Hz", audioInterface->sampleRate); | |||
} | |||
}; | |||
struct BlockSizeItem : MenuItem { | |||
AudioInterface *audioInterface; | |||
int blockSize; | |||
void onAction(EventAction &e) override { | |||
audioInterface->blockSize = blockSize; | |||
audioInterface->openStream(); | |||
} | |||
}; | |||
// void AudioInterface::closeStream() { | |||
// // Clear buffers | |||
// inputBuffer.clear(); | |||
// outputBuffer.clear(); | |||
// inputSrcBuffer.clear(); | |||
// inputSrc.reset(); | |||
// outputSrc.reset(); | |||
// } | |||
struct BlockSizeChoice : ChoiceButton { | |||
AudioInterface *audioInterface; | |||
void onAction(EventAction &e) override { | |||
Menu *menu = gScene->createMenu(); | |||
menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||
menu->box.size.x = box.size.x; | |||
const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096}; | |||
int blockSizesLen = sizeof(blockSizes) / sizeof(blockSizes[0]); | |||
for (int i = 0; i < blockSizesLen; i++) { | |||
BlockSizeItem *item = new BlockSizeItem(); | |||
item->audioInterface = audioInterface; | |||
item->blockSize = blockSizes[i]; | |||
item->text = stringf("%d", blockSizes[i]); | |||
menu->addChild(item); | |||
} | |||
} | |||
void step() override { | |||
this->text = stringf("%d", audioInterface->blockSize); | |||
} | |||
}; | |||
AudioInterfaceWidget::AudioInterfaceWidget() { | |||
@@ -545,69 +212,9 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||
Vec margin = Vec(5, 2); | |||
float labelHeight = 15; | |||
float yPos = margin.y; | |||
float yPos = margin.y + 100; | |||
float xPos; | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(margin.x, yPos); | |||
label->text = "Audio driver"; | |||
addChild(label); | |||
yPos += labelHeight + margin.y; | |||
AudioDriverChoice *choice = new AudioDriverChoice(); | |||
choice->audioInterface = module; | |||
choice->box.pos = Vec(margin.x, yPos); | |||
choice->box.size.x = box.size.x - 2*margin.x; | |||
addChild(choice); | |||
yPos += choice->box.size.y + margin.y; | |||
} | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(margin.x, yPos); | |||
label->text = "Audio device"; | |||
addChild(label); | |||
yPos += labelHeight + margin.y; | |||
AudioDeviceChoice *choice = new AudioDeviceChoice(); | |||
choice->audioInterface = module; | |||
choice->box.pos = Vec(margin.x, yPos); | |||
choice->box.size.x = box.size.x - 2*margin.x; | |||
addChild(choice); | |||
yPos += choice->box.size.y + margin.y; | |||
} | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(margin.x, yPos); | |||
label->text = "Sample rate"; | |||
addChild(label); | |||
yPos += labelHeight + margin.y; | |||
SampleRateChoice *choice = new SampleRateChoice(); | |||
choice->audioInterface = module; | |||
choice->box.pos = Vec(margin.x, yPos); | |||
choice->box.size.x = box.size.x - 2*margin.x; | |||
addChild(choice); | |||
yPos += choice->box.size.y + margin.y; | |||
} | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(margin.x, yPos); | |||
label->text = "Block size"; | |||
addChild(label); | |||
yPos += labelHeight + margin.y; | |||
BlockSizeChoice *choice = new BlockSizeChoice(); | |||
choice->audioInterface = module; | |||
choice->box.pos = Vec(margin.x, yPos); | |||
choice->box.size.x = box.size.x - 2*margin.x; | |||
addChild(choice); | |||
yPos += choice->box.size.y + margin.y; | |||
} | |||
{ | |||
Label *label = new Label(); | |||
label->box.pos = Vec(margin.x, yPos); | |||
@@ -675,4 +282,11 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||
xPos += 37 + margin.x; | |||
} | |||
yPos += 35 + margin.y; | |||
AudioWidget *audioWidget = construct<USB_B_AudioWidget>(); | |||
audioWidget->audioIO = &module->audioIO; | |||
addChild(audioWidget); | |||
// Widget *w = construct<DIN_MIDIWidget>(); | |||
// w->box.pos = Vec(100, 0); | |||
// addChild(w); | |||
} |