Primeira alteração do código original
This commit is contained in:
parent
d8efdf827a
commit
458079bbc8
BIN
ANIMATEDstreamline--ear-hearing-solid.gif
Normal file
BIN
ANIMATEDstreamline--ear-hearing-solid.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 443 B |
160
AudioLinkIcon.svg
Normal file
160
AudioLinkIcon.svg
Normal file
@ -0,0 +1,160 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="113mm"
|
||||
height="113mm"
|
||||
viewBox="0 0 113 113"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.2 (2aeb623e1d, 2025-05-12)"
|
||||
sodipodi:docname="AudioLinkIcon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.69397447"
|
||||
inkscape:cx="6.4843884"
|
||||
inkscape:cy="305.48674"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="970"
|
||||
inkscape:window-x="1280"
|
||||
inkscape:window-y="32"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter51"
|
||||
x="-0.13604602"
|
||||
y="-0.1360563"
|
||||
width="1.3692678"
|
||||
height="1.3061267">
|
||||
<feFlood
|
||||
result="flood"
|
||||
in="SourceGraphic"
|
||||
flood-opacity="0.498039"
|
||||
flood-color="rgb(0,0,0)"
|
||||
id="feFlood50" />
|
||||
<feGaussianBlur
|
||||
result="blur"
|
||||
in="SourceGraphic"
|
||||
stdDeviation="3.500000"
|
||||
id="feGaussianBlur50" />
|
||||
<feOffset
|
||||
result="offset"
|
||||
in="blur"
|
||||
dx="6.000000"
|
||||
dy="2.100000"
|
||||
id="feOffset50" />
|
||||
<feComposite
|
||||
result="comp1"
|
||||
operator="in"
|
||||
in="flood"
|
||||
in2="offset"
|
||||
id="feComposite50" />
|
||||
<feComposite
|
||||
result="comp2"
|
||||
operator="over"
|
||||
in="SourceGraphic"
|
||||
in2="comp1"
|
||||
id="feComposite51" />
|
||||
</filter>
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter62"
|
||||
x="-0.12109337"
|
||||
y="-0.12109314"
|
||||
width="1.2444802"
|
||||
height="1.2444797">
|
||||
<feFlood
|
||||
result="flood"
|
||||
in="SourceGraphic"
|
||||
flood-opacity="0.498039"
|
||||
flood-color="rgb(0,0,0)"
|
||||
id="feFlood61" />
|
||||
<feGaussianBlur
|
||||
result="blur"
|
||||
in="SourceGraphic"
|
||||
stdDeviation="2.200000"
|
||||
id="feGaussianBlur61" />
|
||||
<feOffset
|
||||
result="offset"
|
||||
in="blur"
|
||||
dx="0.100000"
|
||||
dy="0.100000"
|
||||
id="feOffset61" />
|
||||
<feComposite
|
||||
result="comp1"
|
||||
operator="in"
|
||||
in="flood"
|
||||
in2="offset"
|
||||
id="feComposite61" />
|
||||
<feComposite
|
||||
result="comp2"
|
||||
operator="over"
|
||||
in="SourceGraphic"
|
||||
in2="comp1"
|
||||
id="feComposite62" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-51.183197,-13.659061)">
|
||||
<g
|
||||
id="g62"
|
||||
transform="translate(0.02585142,5.4560915)">
|
||||
<path
|
||||
id="path1-6"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:2.72517;filter:url(#filter62)"
|
||||
d="m 115.14877,66.764077 v 16.35103 h 16.35102 v -16.35103 z m 19.07619,0 v 10.90075 h 2.72517 v -2.72524 h 2.72517 v -5.45027 h -2.72517 v -2.72524 z m 2.72517,10.90075 v 5.45028 h 2.72517 v -5.45028 z m 0,5.45028 h -2.72517 v 2.72523 h 2.72517 z m 0,2.72523 v 2.72514 h 2.72517 v 2.72514 h 5.45035 v -5.45028 z m 8.17552,5.45028 v 2.72524 h -2.72518 v 2.72513 h 8.17551 v -5.45037 z m -2.72518,5.45037 h -2.72517 v 2.72514 h 2.72517 z m 0,2.72514 v 2.725243 h 2.72518 v -2.725243 z m 2.72518,2.725243 v 2.72513 h 2.72516 v -2.72513 z m 2.72516,0 h 5.45034 v -2.725243 h -5.45034 z m 5.45034,0 v 8.17551 h 5.45034 v -5.45038 h -2.72517 v -2.72513 z m -5.45034,2.72513 v 5.45038 h 2.72517 v -5.45038 z m -2.72516,0 h -5.45035 v 5.45038 h 5.45035 z m -5.45035,0 v -2.72513 -2.725243 h -5.45034 v 2.725243 h 2.72517 v 2.72513 z m -2.72517,0 h -2.72517 v 5.45038 h 2.72517 z m 2.72517,-8.175513 v -5.45037 h -5.45034 v 5.45037 z m -5.45034,-5.45037 v -5.45028 h -2.72517 v 2.72514 h -2.72516 v 2.72514 z m -5.45033,-2.72514 v -2.72514 h -5.45035 v 2.72514 z m -5.45035,0 h -2.72517 v -2.72514 h -5.45034 v 5.45028 h 8.17551 z m 19.07619,-21.8014 v 16.35103 h 16.35102 v -16.35103 z m -24.52653,2.72524 h 10.90069 v 10.90066 h -10.90069 z m 27.25171,0 h 10.90067 v 10.90066 h -10.90067 z m -24.52654,2.72514 v 5.45037 h 5.45034 v -5.45037 z m 27.2517,0 v 5.45037 h 5.45034 v -5.45037 z m 0,13.62588 v 2.72514 h 5.45034 v 2.72514 h 5.45034 v -5.45028 h -5.45034 z m -32.70204,8.17552 v 16.351023 h 16.35102 V 94.015857 Z m 38.15238,0 v 2.72513 h 2.72517 v 2.72514 h 2.72517 v -5.45027 h -2.72517 z m -35.42721,2.72513 h 10.90069 v 10.900653 h -10.90069 z m 2.72517,2.72514 v 5.450373 h 5.45034 v -5.450373 z" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
fill-rule="evenodd"
|
||||
d="m 60.32428,34.441951 c 2.65442,-5.95163 8.522271,-12.38289 18.278555,-12.38289 13.812456,0 21.584915,10.16621 21.584915,20.30426 0,9.747126 -6.06797,15.954856 -10.417559,19.149446 -2.72904,2.00248 -4.49391,3.58112 -4.49391,5.97487 0,5.95163 -4.86194,10.10566 -9.993892,11.24181 -5.308841,1.174 -11.558635,-0.65124 -14.837013,-7.11101 a 8.6153656,8.6153656 0 0 1 -0.860694,-3.9166 V 38.130261 c 0,-1.1828 0.190092,-2.45888 0.741006,-3.68831 m 13.193071,6.44516 c 0.843094,-2.5474 2.626612,-4.52647 5.169265,-4.52647 3.935077,0 6.249617,3.08283 6.249617,6.61273 0,2.18887 -0.51219,3.45545 -1.08951,4.29836 -0.62308,0.90294 -1.49433,1.584096 -2.70106,2.351856 l -0.61956,0.38722 c -1.02438,0.63364 -2.361007,1.46266 -3.390319,2.46821 a 8.056531,8.056531 0 0 0 -2.468024,5.99352 c 0,1.08598 -0.288658,1.65802 -0.503392,1.93717 a 1.2573777,1.2573777 0 0 1 -0.786769,0.48931 c -0.64772,0.12321 -1.476731,-0.20065 -1.862899,-1.11767 a 2.911011,2.911011 0 1 0 -5.360059,2.27248 7.544266,7.544266 0 0 0 8.275328,4.56854 c 1.652742,-0.30274 3.213254,-1.18807 4.349581,-2.65917 1.131753,-1.47145 1.703783,-3.35759 1.703783,-5.49049 0,-0.93109 0.27986,-1.38696 0.73573,-1.84424 0.51747,-0.50691 1.13703,-0.89413 2.0771,-1.48025 l 0.97862,-0.61428 c 1.36409,-0.87125 3.0596,-2.06302 4.36366,-3.95848 1.35001,-1.95584 2.11882,-4.414706 2.11882,-7.600146 0,-5.93298 -4.16336,-12.43394 -12.066071,-12.43394 -6.240466,0 -9.514269,4.93623 -10.701814,8.5221 a 2.9107262,2.9107262 0 1 0 5.527797,1.82558 m 44.315667,19.950476 a 3.4927158,3.4927158 0 0 1 3.49276,3.49259 19.466069,19.466069 0 0 1 -19.4661,19.46609 3.4927158,3.4927158 0 0 1 0,-6.98534 c 6.89223,0 12.48058,-5.58834 12.48058,-12.48075 a 3.4927158,3.4927158 0 0 1 3.49276,-3.49259 m -7.88898,-0.65652 a 3.4927158,3.4927158 0 0 0 -6.98535,0 c 0,2.81741 -2.28198,5.09939 -5.09939,5.09939 a 3.4927158,3.4927158 0 1 0 0,6.98534 12.084797,12.084797 0 0 0 12.08474,-12.08473"
|
||||
clip-rule="evenodd"
|
||||
id="path1"
|
||||
style="stroke-width:4.65695;filter:url(#filter51)" />
|
||||
</g>
|
||||
</g>
|
||||
<metadata
|
||||
id="metadata62">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<cc:license
|
||||
rdf:resource="http://creativecommons.org/licenses/by/4.0/" />
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://creativecommons.org/licenses/by/4.0/">
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#Distribution" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Notice" />
|
||||
<cc:requires
|
||||
rdf:resource="http://creativecommons.org/ns#Attribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
</svg>
|
After Width: | Height: | Size: 7.9 KiB |
10
README.md
10
README.md
@ -2,4 +2,12 @@
|
||||
|
||||
É uma espécie de "qr code" sonoro. Neste app, você pode gerar um código sonoro a partir de um link e, depois neste mesmo app, ouvir esse código e ir automaticamente para o link.
|
||||
|
||||
Este app é inteiramente baseado em: <https://github.com/ggerganov/ggwave/tree/master>.
|
||||
Este app é inteiramente baseado em: <https://github.com/ggerganov/ggwave/tree/master>.
|
||||
|
||||
O ícone é licenciado sob CC By 4.0 e as imagens presentes no ícone possuem os seguintes créditos e licenças:
|
||||
|
||||
* QRCode icon por Vaadin, licenciada sob a Apache License 2.0.
|
||||
|
||||
* Ear Hearing (solid) icon por Streamline, licenciada sob a Creative Commons Attribution 4.0 International (CC BY 4.0).
|
||||
|
||||
Obs.: A licença do ANIMATEDstreamline--ear-hearing-solid.gif é a mesma do original (CC BY 4.0).
|
170
estilo.css
Normal file
170
estilo.css
Normal file
@ -0,0 +1,170 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
justify-content: space-around;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(83deg,#b32eeb,#5ba4fa,#bc1b60);
|
||||
background-size: 180% 180%;
|
||||
animation: gradient-animation 14s ease infinite;
|
||||
font-family: "Josefin Sans", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 50px;
|
||||
font-family: "Goldman", sans-serif;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 21px;
|
||||
font-family: "Josefin Sans", sans-serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
a.nav-link {
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
font-family: "Josefin Sans", sans-serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.setores {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
#gerarSetor div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
#listeningIcon {
|
||||
visibility: hidden;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-bottom: 15px;
|
||||
border: 4px solid #fff; /* Cor de fundo do círculo */
|
||||
border-top: 4px solid #5ba4fa; /* Cor da parte girante */
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite; /* Animação contínua */
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
#rxData, #rxDataTXT, #countdown {
|
||||
font-size: 27px;
|
||||
font-weight: bold;
|
||||
margin-right: 3px;
|
||||
background-color: rgba(255,255,255,1);
|
||||
color: #5ba4fa;
|
||||
padding: 3px 8px;
|
||||
border-radius: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#redirecionamentoTXT, #divTXT {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@keyframes gradient-animation {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
p, label {
|
||||
margin: 10px;
|
||||
max-width: 767px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
h1 img {
|
||||
height: 2em;
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 83vw;
|
||||
max-width: 600px;
|
||||
height: 40px;
|
||||
border: 2px solid;
|
||||
border-radius: 10px;
|
||||
background: rgba(200, 200, 200, 0.1);
|
||||
color: rgb(255, 255, 255);
|
||||
font-family: "Josefine Sans", sans-serif;
|
||||
}
|
||||
|
||||
.button-34 {
|
||||
margin: 10px 10px 25px 10px;
|
||||
background: #5E5DF0;
|
||||
border-radius: 999px;
|
||||
box-shadow: #5E5DF0 0 10px 20px -10px;
|
||||
box-sizing: border-box;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
font-family: Inter,Helvetica,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Noto Color Emoji","Segoe UI Symbol","Android Emoji",EmojiSymbols,-apple-system,system-ui,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans",sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
opacity: 1;
|
||||
outline: 0 solid transparent;
|
||||
padding: 8px 18px;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
touch-action: manipulation;
|
||||
width: fit-content;
|
||||
word-break: break-word;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.button-34:hover {
|
||||
box-shadow: #5E5DF0 0 10px 20px -10px;
|
||||
opacity: 0.8;
|
||||
}
|
241
index.html
241
index.html
@ -1,40 +1,79 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<html lang="pt-br">
|
||||
<head>
|
||||
<title>ggwave : javascript example</title>
|
||||
<title>Moan Áudio Link</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/svg+xml" href="AudioLinkIcon.svg">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Goldman:wght@400;700&family=Josefin+Sans:ital,wght@0,100..700;1,100..700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="estilo.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-container">
|
||||
Minimal <b>ggwave</b> example using Javascript bindings
|
||||
<h1>
|
||||
<img src="AudioLinkIcon.svg" alt="Ícone De uma orelha ouvindo um qr code">
|
||||
Moan Áudio Link
|
||||
</h1>
|
||||
|
||||
|
||||
<div id="capturaSetor" class="setores">
|
||||
|
||||
<br><br>
|
||||
<img id="listeningIcon" src="ANIMATEDstreamline--ear-hearing-solid.gif" alt="Ouvindo">
|
||||
|
||||
<button id="captureStart" class="button-34">Capturar</button>
|
||||
<button id="captureStop" class="button-34" hidden>Parar</button>
|
||||
|
||||
<div>Tx Data:</div> <textarea name="textarea" id="txData" style="width:300px;height:100px;">Hello javascript</textarea><br>
|
||||
|
||||
<button onclick="onSend();">Send</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div>Rx data:</div> <textarea name="textarea" id="rxData" style="width:300px;height:100px;" disabled></textarea><br>
|
||||
|
||||
<button id="captureStart">Start capturing</button>
|
||||
<button id="captureStop" hidden>Stop capturing</button>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div class="cell-version">
|
||||
<span>
|
||||
|
|
||||
Build time: <span class="nav-link">@GIT_DATE@</span> |
|
||||
Commit hash: <a class="nav-link" href="https://github.com/ggerganov/ggwave/commit/@GIT_SHA1@">@GIT_SHA1@</a> |
|
||||
Commit subject: <span class="nav-link">@GIT_COMMIT_SUBJECT@</span> |
|
||||
<a class="nav-link" href="https://github.com/ggerganov/ggwave/tree/master/examples/ggwave-js">Source Code</a> |
|
||||
</span>
|
||||
<!-- Radio buttons -->
|
||||
<div>
|
||||
<label>
|
||||
<input type="radio" name="modoCaptura" value="link" checked>
|
||||
Link automático
|
||||
</label>
|
||||
<label style="margin-left: 1em;">
|
||||
<input type="radio" name="modoCaptura" value="texto">
|
||||
Somente texto
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="redirecionamentoTXT" class="setores">
|
||||
<div class="spinner"></div>
|
||||
<p>Redirecionando para <span id="rxData"></span> em <span id="countdown">3</span> segundo(s).</p>
|
||||
</div>
|
||||
|
||||
<div id="divTXT" class="setores">
|
||||
<p>O código é <span id="rxDataTXT">...</span></p>
|
||||
</div>
|
||||
|
||||
<div id="gerarSetor" class="setores">
|
||||
<h2>Gerar código sonoro</h2>
|
||||
<textarea name="textarea" id="txData">https://livro.online</textarea><br>
|
||||
|
||||
<div>
|
||||
<button class="button-34" onclick="onSend();">Gerar</button>
|
||||
<button class="button-34" onclick="downloadAsMP3()">Download</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p>É uma espécie de "qr code" sonoro. Neste app, você pode gerar um código sonoro a partir de um link e, depois neste mesmo app, ouvir esse código e ir automaticamente para o link.</p>
|
||||
|
||||
<a class="nav-link" href="https://gitea.livro.online/editoramoan/moan-audio-link">Código fonte</a>
|
||||
|
||||
|
||||
|
||||
<script type="text/javascript" src="ggwave.js"></script>
|
||||
<script src="https://unpkg.com/lamejs@1.2.0/lame.min.js"></script>
|
||||
|
||||
<script type='text/javascript'>
|
||||
|
||||
function getModoCapturaSelecionado() {
|
||||
const selecionado = document.querySelector('input[name="modoCaptura"]:checked');
|
||||
return selecionado ? selecionado.value : null;
|
||||
}
|
||||
|
||||
|
||||
window.AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
window.OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext;
|
||||
|
||||
@ -54,8 +93,17 @@
|
||||
|
||||
var txData = document.getElementById("txData");
|
||||
var rxData = document.getElementById("rxData");
|
||||
var captureStart = document.getElementById("captureStart");
|
||||
var rxDataTXT = document.getElementById("rxDataTXT");
|
||||
var listeningIcon = document.getElementById("listeningIcon");
|
||||
var countdown = document.getElementById("countdown");
|
||||
var redirecionamentoTXT = document.getElementById("redirecionamentoTXT");
|
||||
var capturaSetor = document.getElementById("capturaSetor");
|
||||
var captureStop = document.getElementById("captureStop");
|
||||
let count = 3; // 3 segundos para o countdown
|
||||
const countdownEl = document.getElementById("countdown");
|
||||
const radios = document.querySelectorAll('input[name="modoCaptura"]');
|
||||
const divTXT = document.getElementById("divTXT");
|
||||
|
||||
|
||||
// helper function
|
||||
function convertTypedArray(src, type) {
|
||||
@ -76,10 +124,31 @@
|
||||
}
|
||||
}
|
||||
|
||||
//Função para atualizar a visibilidade da divTXT
|
||||
function atualizarVisibilidadeDivTXT() {
|
||||
const selecionado = document.querySelector('input[name="modoCaptura"]:checked');
|
||||
if (selecionado?.value === "texto") {
|
||||
divTXT.style.display = "FLEX";
|
||||
} else {
|
||||
divTXT.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// Executa ao mudar os radio buttons
|
||||
radios.forEach(radio => {
|
||||
radio.addEventListener("change", atualizarVisibilidadeDivTXT);
|
||||
});
|
||||
|
||||
// Executa uma vez ao carregar a página
|
||||
document.addEventListener("DOMContentLoaded", atualizarVisibilidadeDivTXT);
|
||||
|
||||
|
||||
//
|
||||
// Tx
|
||||
//
|
||||
|
||||
let lastAudioBuffer = null; // variável global
|
||||
|
||||
function onSend() {
|
||||
init();
|
||||
|
||||
@ -93,12 +162,60 @@
|
||||
var buf = convertTypedArray(waveform, Float32Array);
|
||||
var buffer = context.createBuffer(1, buf.length, context.sampleRate);
|
||||
buffer.getChannelData(0).set(buf);
|
||||
|
||||
// salvar buffer para download posterior
|
||||
lastAudioBuffer = buffer;
|
||||
|
||||
var source = context.createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.connect(context.destination);
|
||||
source.start(0);
|
||||
}
|
||||
|
||||
//Dar a opção de download
|
||||
function downloadAsMP3() {
|
||||
if (!lastAudioBuffer) {
|
||||
alert("Nenhum áudio gerado ainda.");
|
||||
return;
|
||||
}
|
||||
|
||||
const samples = lastAudioBuffer.getChannelData(0);
|
||||
const mp3encoder = new lamejs.Mp3Encoder(1, lastAudioBuffer.sampleRate, 128); // mono, sampleRate, 128kbps
|
||||
const blockSize = 1152;
|
||||
let mp3Data = [];
|
||||
|
||||
for (let i = 0; i < samples.length; i += blockSize) {
|
||||
const sampleChunk = samples.subarray(i, i + blockSize);
|
||||
const int16Chunk = floatTo16BitPCM(sampleChunk);
|
||||
const mp3buf = mp3encoder.encodeBuffer(int16Chunk);
|
||||
if (mp3buf.length > 0) mp3Data.push(mp3buf);
|
||||
}
|
||||
|
||||
const mp3buf = mp3encoder.flush();
|
||||
if (mp3buf.length > 0) mp3Data.push(mp3buf);
|
||||
|
||||
const blob = new Blob(mp3Data, { type: 'audio/mp3' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "moan-audio-link.mp3";
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// utilitário para converter float [-1, 1] para int16
|
||||
function floatTo16BitPCM(float32Array) {
|
||||
const output = new Int16Array(float32Array.length);
|
||||
for (let i = 0; i < float32Array.length; i++) {
|
||||
let s = Math.max(-1, Math.min(1, float32Array[i]));
|
||||
output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// Rx
|
||||
//
|
||||
@ -137,28 +254,56 @@
|
||||
recorder.onaudioprocess = function (e) {
|
||||
var source = e.inputBuffer;
|
||||
var res = ggwave.decode(instance, convertTypedArray(new Float32Array(source.getChannelData(0)), Int8Array));
|
||||
var link;
|
||||
|
||||
if (res && res.length > 0) {
|
||||
res = new TextDecoder("utf-8").decode(res);
|
||||
rxData.value = res;
|
||||
|
||||
if (getModoCapturaSelecionado() == "link") {//Se é link automático
|
||||
|
||||
res = new TextDecoder("utf-8").decode(res);
|
||||
// Remove "http://" ou "https://" do início da string
|
||||
res = res.replace(/^https?:\/\//, "");
|
||||
|
||||
// Adiciona "https://" no início
|
||||
res = "https://" + res;
|
||||
|
||||
// Adiciona tag a
|
||||
link = "<a src=\"" + res + "\">" + res + "</a>";
|
||||
|
||||
// remove o primeiro setor. O setor de captura
|
||||
capturaSetor.style.display = "none";
|
||||
|
||||
// Mostra o redirecionamento
|
||||
redirecionamentoTXT.style.display = "flex";
|
||||
|
||||
// Atribui o valor ao rxData
|
||||
rxData.innerHTML = link;
|
||||
|
||||
//countdown e redirecionamento em 3 segundos
|
||||
const interval = setInterval(() => {
|
||||
count--;
|
||||
countdownEl.textContent = count;
|
||||
|
||||
if (count <= 0) {
|
||||
clearInterval(interval);
|
||||
window.location.href = res;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Aguarda 3 segundos e redireciona
|
||||
setTimeout(() => {
|
||||
window.location.href = res;
|
||||
}, 3000);
|
||||
} else if (getModoCapturaSelecionado() == "texto") { //Se é somente texto
|
||||
res = new TextDecoder("utf-8").decode(res);
|
||||
|
||||
|
||||
// Mostra o texto
|
||||
divTXT.style.display = "flex";
|
||||
rxDataTXT.innerHTML = res;
|
||||
}
|
||||
}
|
||||
|
||||
// obsolete javascript resampling
|
||||
// since ggwave v0.2.0 the resampling is built-in ggwave
|
||||
//var offlineCtx = new OfflineAudioContext(source.numberOfChannels, 48000*source.duration, 48000);
|
||||
//var offlineSource = offlineCtx.createBufferSource();
|
||||
|
||||
//offlineSource.buffer = source;
|
||||
//offlineSource.connect(offlineCtx.destination);
|
||||
//offlineSource.start();
|
||||
//offlineCtx.startRendering();
|
||||
//offlineCtx.oncomplete = function(e) {
|
||||
// var resampled = e.renderedBuffer.getChannelData(0);
|
||||
// var res = ggwave.decode(instance, convertTypedArray(new Float32Array(resampled), Int8Array));
|
||||
// if (res) {
|
||||
// rxData.value = res;
|
||||
// }
|
||||
//};
|
||||
|
||||
}
|
||||
|
||||
mediaStream.connect(recorder);
|
||||
@ -167,7 +312,7 @@
|
||||
console.error(e);
|
||||
});
|
||||
|
||||
rxData.value = 'Listening ...';
|
||||
listeningIcon.style.visibility = "visible";
|
||||
captureStart.hidden = true;
|
||||
captureStop.hidden = false;
|
||||
});
|
||||
@ -179,7 +324,7 @@
|
||||
recorder = null;
|
||||
}
|
||||
|
||||
rxData.value = 'Audio capture is paused! Press the "Start capturing" button to analyze audio from the microphone';
|
||||
listeningIcon.style.visibility = "hidden";
|
||||
captureStart.hidden = false;
|
||||
captureStop.hidden = true;
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user