tag:blogger.com,1999:blog-45166120961575022732024-03-24T22:01:43.993-06:00Xwork's BlogThe lord is waiting to take your hand.Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.comBlogger194125tag:blogger.com,1999:blog-4516612096157502273.post-81255368184682939012024-03-24T22:01:00.000-06:002024-03-24T22:01:12.741-06:00El Video Correcto #10 - VEncoder y subtitulos.<p>Hola de nuevo a todos, el día de hoy vamos a ver cómo insertar subtitulos en los videos que se codifiquen con VEncoder.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwKz61hrDB3W68MwjW8orxgVpjS48rlmubN4rnfQ90wgvTI6kp_6sL7MX6LpGue9LlDFY1ODzYCnZ6Z9coN7ziwESbL7N6wxzhlXIbeRvZC30J6BoO-7F1HypXBgNuMVGnHoD5h5aIa6qltUJz-ImTrWoD-0ESFa9H397iZm6TCM1__9D9QbUZrNohXZ6C/s1920/Captura%20de%20pantalla%202024-03-24%2014.33.15.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1040" data-original-width="1920" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwKz61hrDB3W68MwjW8orxgVpjS48rlmubN4rnfQ90wgvTI6kp_6sL7MX6LpGue9LlDFY1ODzYCnZ6Z9coN7ziwESbL7N6wxzhlXIbeRvZC30J6BoO-7F1HypXBgNuMVGnHoD5h5aIa6qltUJz-ImTrWoD-0ESFa9H397iZm6TCM1__9D9QbUZrNohXZ6C/s320/Captura%20de%20pantalla%202024-03-24%2014.33.15.png" width="320" /></a></div><br /><p>Hay que recordar que VEncoder es una interfaz de usuario para FFMPEG; si bien la idea es mantener los mas simple posible todo, para lograr resultados complejos, es necesario hacer ajustes complejos. Antes de continuar, me gustaría recapitular cual fue la idea de VEncoder. El programa inicialmente nació cómo una herramienta para convertir los videos para que el Play Station Portable pudiera reproducirlos, incluso era parte de un programa llamado PSP Manager, y cómo su nombre lo pudiera sugerir, servía para manejar el contenido multimedia de la consola. Poco a poco cada una de las partes se separó y dieron origen a VEncoder y AEncoder que a diferencia de PSP Manager, no tuvieron tanta relevancia.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnTxH5QrR42rFzeIrp87Jfgwl0KijcYI83LhsyR4LALhk1qCp7ACf0r9WudHlvAmR9oXt0oDTdRucEuycjy19EfJ0lTggG0XuvfBIx76bpjTU7Wrvf0qT72Qvv9WPK530EMttuoCmr1iGgnvkq0HthsjI5Nq-x6dozfx5q8wz_45fLg5sE8dk5og_ksUIX/s723/Captura%20de%20pantalla%202024-03-24%2014.44.39.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="397" data-original-width="723" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnTxH5QrR42rFzeIrp87Jfgwl0KijcYI83LhsyR4LALhk1qCp7ACf0r9WudHlvAmR9oXt0oDTdRucEuycjy19EfJ0lTggG0XuvfBIx76bpjTU7Wrvf0qT72Qvv9WPK530EMttuoCmr1iGgnvkq0HthsjI5Nq-x6dozfx5q8wz_45fLg5sE8dk5og_ksUIX/s320/Captura%20de%20pantalla%202024-03-24%2014.44.39.png" width="320" /></a></div><br /><p>He usado mi PSP desde el 2010 y de las pocas cosas que no he logrado entender, es cómo funciona la opción de los subtítulos en el reproductor de video.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga2iqi2rLCLB9WyVCyhdbdw37banYCMDPikuhrqrz1ArXvHCRW5qyj29Bhnn4QEZsg_9LhVFQFxAFoVr8YrEBG-gLQI4ISHGAzCbKC9waFWE92_i_To0ZGc1Nm9bUIX30_pGRngGNykTc1xknko9bYsv0Azj_2ICXg_gAFZAxYCtOSBhuoIbqNj-DpT9MH/s496/Captura%20de%20pantalla%202024-03-24%2014.55.47.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="311" data-original-width="496" height="201" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga2iqi2rLCLB9WyVCyhdbdw37banYCMDPikuhrqrz1ArXvHCRW5qyj29Bhnn4QEZsg_9LhVFQFxAFoVr8YrEBG-gLQI4ISHGAzCbKC9waFWE92_i_To0ZGc1Nm9bUIX30_pGRngGNykTc1xknko9bYsv0Azj_2ICXg_gAFZAxYCtOSBhuoIbqNj-DpT9MH/s320/Captura%20de%20pantalla%202024-03-24%2014.55.47.png" width="320" /></a></div><br /><p>Recientemente me he encontrado con gente para la cual la consola es algo totalmente nuevo y he compartido algunos videos con esas personas, pero cómo los tiempos cambian, ahora resulta más sencillo encontrar contenido que no está en nuestro idioma por lo que resulta necesario encontrar una forma de incluirlos. Cómo mencioné hace poco, a pesar de que la PSP tiene (o eso según el menú) la capacidad de reproducir subtítulos, nunca supe cómo usarlos. </p><p>FFMPEG tiene la capacidad de reproducir subtítulos, pero antes de detallar cómo es que funcionan, hay que resaltar algo importante. Hay dos formas de incrustar los subtítulos; la primera es sencilla, se dota de un archivo que tiene la transcripción de los diálogos con los tiempos para cada uno, el reproductor los lee y los muestra sobre el video, a esto le podemos llamar "subtítulos por software". La otra forma es similar, tenemos un archivo con las transcripciones pero solo las usamos al momento de codificar el video y justo en la codificación, vamos a modificar la imagen correspondiente al tiempo del fotograma, de tal forma que el texto quede cómo parte de la misma, con eso bastará para que los subtítulos queden de forma permanente en el video.</p><p>Para lograr esto, usaremos FFMPEG junto con VEncoder (el cual actualizare pronto para que la opción "Si hay subtítulos 'quemarlos'" funcione). En VEncoder, hay una ficha llamada "Parámetros adicionales" dentro, hay una ficha llamada "video". FFMPEG soporta varios tipos de subtítulos, archivos SRT o archivos ASS, los primeros son mas sencillos, pues solo incluyen el dialogo y el tiempo en que deben de aparecer; los segundos incluyen "estilos" y efectos (transiciones, diferentes ubicaciones para el texto).</p><p>Primero veamos un ejemplo de cómo insertar un archivo SRT.</p><div><pre> -vf "subtitles='I\:\\\VIDEOS\\\Track3.srt'"
</pre></div><div>En VEncoder, bastará con agregar la línea anterior a los parámetros adicionales del video, para este caso, tenemos que nuestro archivo de subtítulos se llama "Track3.srt" y que su ruta completa es "I:\VIDEOS\Track3.srt", en FFMPEG para escapar las contra-diagonales, necesitamos usar doble "\". Supongamos que tenemos la siguiente ruta "c:\VIDEOS \Dr House\Season 1\Pilot.srt", la ruta para usarla en FFMPEG quedaria: "c\:\\\VIDEOS\\\Dr House\\\Season 1\\\Pilot.srt".</div><div><br /></div><div>Si usamos archivos ASS, la sintaxis es similar.</div><div><br /></div><div><pre>-vf "ass='I\:\\\VIDEOS\\\Track3.srt'"</pre></div><div><br /></div><div>La codificación para los archivos ass tardará mas tiempo, dependiendo de los estilos y los efectos, por lo que en la medida de lo posible recomiendo usar archivos SRT.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyxKdzDPR_UKk6Ff6uCz2bSUzCywwgarfJzA5-ezhHDkAl8X9zkhFaVrhK08mETDm4X0m3mRxKCIN783FGJ3ntILKenuRs_cI8jsJEse3tIOria3nMfu_mLp8Niaji4ee6Y9nnSPPuTpxXN_iwIjDYPoRjf8M_YoV-YlqSI9TbMYHuf_6_UWM-y7WbjAk1/s960/20240324154712.fw.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="272" data-original-width="960" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyxKdzDPR_UKk6Ff6uCz2bSUzCywwgarfJzA5-ezhHDkAl8X9zkhFaVrhK08mETDm4X0m3mRxKCIN783FGJ3ntILKenuRs_cI8jsJEse3tIOria3nMfu_mLp8Niaji4ee6Y9nnSPPuTpxXN_iwIjDYPoRjf8M_YoV-YlqSI9TbMYHuf_6_UWM-y7WbjAk1/w400-h114/20240324154712.fw.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">No es precisamente el mismo cuadro, pero se ven lo subtítulos "quemados"</td></tr></tbody></table><div><br /></div><div>Y bien, por ahora es todo, aun quedan varias cosas que tratar para poder visualizar los videos de forma correcta en el PSP, una de las cosas más comunes, es encontrar la relación de aspecto adecuada para cada video, la resolucion y la calidad, pero eso lo veremos en el siguiente post.</div><div><br /></div><div>Los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-53949173460678506232024-02-25T10:52:00.002-06:002024-02-25T10:52:22.035-06:00Learning machine #18 - Programando en Pascal - Estructuras de datos (Record).<p>Hola de nuevo a todos, el día de hoy vamos a continuar con mas del aprendizaje del lenguaje de programación en pascal.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_l58XI5paZONtpRzHZ5dNxIBVP85o2j3zNV8qTMrS5Zp2N4Ds0OV9EgqDWli9u5MhWz4TMto5pRO02J9b7WU0rNOg0sNu0TvmEOVm-Tqlg20QzNHcNZGfrxtTYmwgfwIVsbjmNxOjWcqDd_jU30fSzkOAgyyOQ6ocjmQVlLJdeoiwem-U4qps68n91mMV/s1129/Captura%20de%20pantalla%202024-02-25%2009.53.40.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="635" data-original-width="1129" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_l58XI5paZONtpRzHZ5dNxIBVP85o2j3zNV8qTMrS5Zp2N4Ds0OV9EgqDWli9u5MhWz4TMto5pRO02J9b7WU0rNOg0sNu0TvmEOVm-Tqlg20QzNHcNZGfrxtTYmwgfwIVsbjmNxOjWcqDd_jU30fSzkOAgyyOQ6ocjmQVlLJdeoiwem-U4qps68n91mMV/s320/Captura%20de%20pantalla%202024-02-25%2009.53.40.png" width="320" /></a></div><p>"En el capítulo anterior de <<Learning machine>>" vimos cómo es que se usan los arreglos de una o varias dimensiones. Hoy vamos a ver cómo es que se usan las estructuras de datos o "<i>Record</i>" (registro en español). Un registro básicamente es un conjunto de variables bajo un mismo nombre o tipo.</p><p>Para crear un registro, hay que crear un tipo usando la palabra "<i>Type</i>" seguido del nombre o identificador que le daremos al registro, hasta aquí todo es igual a cuando creamos los tipos, ahora la diferencia viene en que hay que asignarlo al tipo "<i>Record</i>", es decir tendríamos algo cómo: "<i>Type</i> NombreDelRegistro <i>= Record</i>" una vez creado, dentro de este nuevo tipo, agregaremos "campos"; cada campo sera una variable de cualquiera de los tipos soportados por Pascal. Una vez agregados todos los campos, finalizaremos la declaracion, con la palabra reservada "<i>end</i>;". Para apreciar un poco mejor cómo es todo el proceso, tomemos el siguiente ejemplo:</p><div><pre>Type MiCancion = Record
Cancion,Artista,Album : string;
Year : Integer;
BeatPerMinute : single
end;</pre></div><p>Con esto habremos creado un nuevo registro llamado "<i>MiCancion</i>"; este contendrá los campos: "<i>Cancion, Artista, Album, Year </i>y<i> BeatPerMinute</i>" del tipo "<i>string, string, string, integer </i>y<i> single</i>" respectivamente. Para poder usar el registro, crearemos variables del tipo que acabamos de crear.</p><p>Si queremos acceder a los campos, bastará con escribir el nombre de la variable seguido de "." y despues el nombre del campo. Para visualizar mejor cómo es que se hace, observemos el siguiente código:</p><div><pre><span> </span><span> </span>KillerQueen.Cancion := 'Killer Queen.flac';
KillerQueen.Artista := 'Queen';
KillerQueen.Album := 'Sheer Heart Attack';
KillerQueen.Year := 1974;
KillerQueen.BeatPerMinute := 117.27;
writeln(KillerQueen.Cancion);
writeln(KillerQueen.Artista);
writeln(KillerQueen.Album);
writeln(KillerQueen.Year);
writeln(KillerQueen.BeatPerMinute);</pre></div><p>Una forma de agilizar el acceso al registro, es hacer uso de la palabra reservada "<i>with</i>", para ello, escribiremos "<i>with</i>" seguido del nombre del registro al cual queremos acceder seguido de de la palabra reservada "<i>do</i>", luego "<i>begin</i>", para poder acceder a los miembros solo bastará con escribir el nombre de cada campo; y cómo cada sección, la finalizaremos con su respectivo "<i>end;</i>". Ahora revisemos el siguiente código que ejemplifica lo anterior.</p><div><pre>with Epitaph do
begin
Cancion := 'Epitaph.flac';
Artista := 'King Crimson';
Album := 'In the court of the crimson king';
Year := 1969;
BeatPerMinute := 100.37;
end;</pre></div><p>Los registros son utiles a la hora de trabajar con estructuras de datos ya que una vez que se crean y se asignan los valores correspondientes, todo el conjunto pasara a ser parte de la misma variable y si por ejemplo asignamos "<i>KillerQueen := Epitaph</i>" tendremos una copia exacta del primero.</p><p>Solo por si se ofrece, dejo el código de un programa completo que hace uso de los registros.</p><div><pre>program Registros(output);
uses crt, sysutils;
Type MiCancion = Record
Cancion,Artista,Album : string;
Year : Integer;
BeatPerMinute : single
end;
var
KillerQueen, Epitaph : MiCancion;
begin
KillerQueen.Cancion := 'Killer Queen.flac';
KillerQueen.Artista := 'Queen';
KillerQueen.Album := 'Sheer Heart Attack';
KillerQueen.Year := 1974;
KillerQueen.BeatPerMinute := 117.27;
writeln(KillerQueen.Cancion);
writeln(KillerQueen.Artista);
writeln(KillerQueen.Album);
writeln(KillerQueen.Year);
writeln(KillerQueen.BeatPerMinute);
with Epitaph do
begin
Cancion := 'Epitaph.flac';
Artista := 'King Crimson';
Album := 'In the court of the crimson king';
Year := 1969;
BeatPerMinute := 100.37;
end;
writeln(Epitaph.Cancion);
writeln(Epitaph.Artista);
writeln(Epitaph.Album);
writeln(Epitaph.Year);
writeln(Epitaph.BeatPerMinute);
readln();
end.</pre></div><p>Y bien, por ahora es todo. El código lo puedes copiar para probarlo. Antes de terminar, hay que aclarar algo, si bien en español, se puede leer "<i>Record</i>" cómo registro, no hay que confundirlo con "Register" que es una cosa completamente diferente (que eventualmente veremos).</p><p>Los leo luego.</p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-34460810347026396082024-01-01T12:30:00.003-06:002024-01-01T12:30:27.507-06:00Bienvenido el 2024<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgZV3hkt-B0HMDhL4FwdSOeYAAy4pc4fvrpTyTqNw-4SyQnZr-eUsOoEd5GLgCQVyvwrarNonBYn_RyNmLoVqC0pChCSS1fm2kXULD6wsND1LQ653MKSnuz-ld38ztQ9IvFl4ne1mKvgUKUEEonDTTMWc3CFDYvqukAemhplmkNxANdV9aziEep_ulRUDF/s1920/X2024.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgZV3hkt-B0HMDhL4FwdSOeYAAy4pc4fvrpTyTqNw-4SyQnZr-eUsOoEd5GLgCQVyvwrarNonBYn_RyNmLoVqC0pChCSS1fm2kXULD6wsND1LQ653MKSnuz-ld38ztQ9IvFl4ne1mKvgUKUEEonDTTMWc3CFDYvqukAemhplmkNxANdV9aziEep_ulRUDF/w400-h225/X2024.png" width="400" /></a></div><br /><p>Hola de nuevo a todos, el día de hoy solo escribo para desearles a todos un buen año 2024. Cómo ya se va haciendo costumbre este año trataré de publicar al menos una entrada al mes; el año 2023 fue particularmente bueno y contrario a lo que se pensaría, esa fue la razón por la cual la actividad del blog bajo a niveles no vistos antes. Es bueno ver que aun hay gente que espera seguir leyendo los artículos y que los temas son bastante atinados para ellos. Sin más que decir, espero que sigan pasando a leer.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIROgsx3biAXgrnZlX7wolxN5jaofv6ZWtM_V-O0kwfil8QoT-SY5TcwBQc6PlGRP5rJIb4iQ0kR82ZCBMBXb-MT8rxp28SGUGcaNvol-soFjGAOup7l4cPfc_DwuJ_6hF9TjAdOiZREuurIZw9wp6QDQCToFWKo83x2Iyjzmzfm56bPoCwdK9uH6GFMKE/s480/20240101112642.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="272" data-original-width="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIROgsx3biAXgrnZlX7wolxN5jaofv6ZWtM_V-O0kwfil8QoT-SY5TcwBQc6PlGRP5rJIb4iQ0kR82ZCBMBXb-MT8rxp28SGUGcaNvol-soFjGAOup7l4cPfc_DwuJ_6hF9TjAdOiZREuurIZw9wp6QDQCToFWKo83x2Iyjzmzfm56bPoCwdK9uH6GFMKE/s16000/20240101112642.png" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Si, 2024 y sigo usando mi PSP.</td></tr></tbody></table><br /><p><br /></p><p>Los leo luego.</p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-9029304878454337482023-12-31T15:17:00.000-06:002023-12-31T15:17:41.549-06:00Vamos a programar #104 - Leyendo metadatos de archivos FLAC (Pt. 3 Ver. C#)<p> Hola de nuevo a todos, el día de hoy vamos a continuar con la última parte de FlacTagReader.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY-wlTYJ0syUSfviLjfzrdnb5wO2C3JA_yIFqxAC3N0u6co8ONoEPr01wcJpMg8xaB642Id8DhrClBMpfsNjWFqxoa5ulsq-puQme0Tpr9q1tntyThZFjoprmzPmsJzitXBmRbs3j_RpqsgXUVX9QBvRYzZtdmNfgLVgBOe22o_MJkSaBTXs8g1Q3iq7Z9/s970/Captura%20de%20pantalla%202023-12-31%2012.01.32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="700" data-original-width="970" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjY-wlTYJ0syUSfviLjfzrdnb5wO2C3JA_yIFqxAC3N0u6co8ONoEPr01wcJpMg8xaB642Id8DhrClBMpfsNjWFqxoa5ulsq-puQme0Tpr9q1tntyThZFjoprmzPmsJzitXBmRbs3j_RpqsgXUVX9QBvRYzZtdmNfgLVgBOe22o_MJkSaBTXs8g1Q3iq7Z9/s320/Captura%20de%20pantalla%202023-12-31%2012.01.32.png" width="320" /></a></div><br /><p>En el post anterior vimos cómo es que funcionan algunas funciones (valga la redundancia) y hoy veremos las faltantes.</p><p>La siguiente función es: "<i>NumberToBlockType</i>()". Esta función sirve para convertir un número en su equivalente al de la numeración "<i>BlockTypes</i>". Recibe un parámetro del tipo "<i>uint</i>" que es el número que queremos convertir. Regresa un valor del tipo enumeración "<i>BlockTypes</i>" equivalente. Hay que recordar que nos basamos en la de los tipos de bloques de la documentación.</p><p style="text-align: left;">La siguiente función es: "<i>ReadVorbisData</i>()". Cómo su nombre lo podría sugerir, esta función sirve para leer los datos de tipo vorbis ¿Qué es esto? te preguntaras, este bloque en particular es el que contiene los metadatos tales cómo: álbum, artista, año, etc. Siguen la estructura para los comentarios en archivos ogg vorbis.</p><p style="text-align: left;">La estructura de los comentarios es cómo la que sigue:</p><p style="text-align: left;"></p><ul style="text-align: left;"><li>Tamaño de la descripción del proveedor (32 bits).</li><li>Cadena de texto para describir al proveedor.</li><li>Tamaño de la lista de comentarios del usuario (32 bits).</li><li>Tamaño de la lista de comentarios.</li><ul><li>Tamaño del elemento actual.</li><li>Cadena de texto en formato UTF-8</li></ul><li>Marcador de final del bloque (si es verdadero o no hay se marca error o fin del bloque).</li></ul><div>Cada elemento de la lista anterior consta de 3 partes:</div><div><ul style="text-align: left;"><li>Nombre del campo.</li><li>= (símbolo de igual o símbolo ASCII 0x3D).</li><li>Descripción del campo.</li></ul><br /></div><div>Aunque no es mandatorio seguir una estructura particular para cada nombre de campo, si se recomiendan los siguientes:</div><div><ul style="text-align: left;"><li>Los nombres de cada campo no deben de contener símbolos que escapen del estándar ASCII de caracteres imprimibles, es decir: de 0x20 a 0x7D (excluyendo el símbolo de igual "=" o 0x3D) y de 0x41 a 0x5A.</li><li>Se pueden usar los siguientes nombres de campo:</li><ul><li>TITLE</li><li>VERSION</li><li>ALBUM</li><li>TRACKNUMBER</li><li>ARTIST</li><li>PERFORMER</li><li>COPYRIGHT</li><li>LICENSE</li><li>ORGANIZATION</li><li>DESCRIPTION</li><li>GENRE</li><li>DATE</li><li>LOCATION</li><li>CONTACT</li><li>ISRC</li></ul><li>Aunque los campos anteriores son sugeridos, podemos usar los propios (sin abusar de ello) y por ejemplo, un campo llamado <i>BEATPERMINUTE, </i>es totalmente válido.</li></ul>Una vez que hemos visto cómo esta estructurado los comentarios para los archivos OGG Vorbis, analicemos la función "<i>ReadVorbisData</i>()". La función "<i>ReadVorbisData</i>()" sirve para leer un bloque que contenga los datos para los comentarios de los archivos ogg (y FLAC). Recibe un arreglo del tipo "<i>byte</i>" que contiene el bloque completo con los datos.</div><div>Primero creamos las variables del tipo "<i>uint</i>" llamadas: "<i>NumberOfFields</i>", "<i>CurrentField</i>" y "<i>CurrentPosition</i>"; las inicializamos con los valores de cero, uno y cero respectivamente. La variable "<i>NumberOfFields</i>" servirá para almacenar la cantidad de comentarios contenidos en el bloque; la variable "<i>CurrentField</i>" llevará la cuenta de cual es el número de comentario que se procesa al momento; la variable "<i>CurrentPosition</i>" llevará la posición en la que nos ubicamos dentro de la secuencia.</div><div>Luego procedemos a leer los datos, si recordamos la descripción, lo primero que debemos de buscar es la secuencia que contiene el tamaño de la descripción del proveedor y deberían de ser los primeros cuatro bytes de la secuencia. Creamos una variable del tipo "<i>uint</i>" llamada "<i>CurrentSize</i>" y tal cómo su nombre los indica, nos servirá para ir almacenando el tamaño del comentario actual, procedemos a hacer la lectura y tendremos el tamaño de la cadena de texto siguiente, por lo que podemos desplazarnos en el bloque cuatro bytes, por lo que a la variable "<i>CurrentPosition</i>" la aumentaremos en cuatro. Para almacenar la cadena de texto que vamos a leer, creamos una nueva variable llamada "<i>CurrentChunk</i>" del tipo "<i>byte</i>[]" y reservaremos espacio de acuerdo al tamaño actual del comentario por lo que la asignamos el tamaño de "<i>CurrentSize</i>". Después copiamos del arreglo "<i>TheData</i>" (que es parámetro que se paso a la función) al arreglo "<i>CurrentChunk</i>" la cantidad de bytes marcada por "<i>CurrentSize</i>", creamos una nueva variable del tipo "<i>string</i>" llamada "<i>CurrentText</i>" y la asignaremos con el resultado de la llamada a la función "<i>Encoding.UTF8.GetString</i>()" con el parámetro "<i>CurrentChunk</i>".</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD1g496RlKlJddsGjYj0D3IX9KHQka7Xw4CiaA2iAdGv3whv6dFaBTkGO6-UPdhSE1iQL7KWFrUJSkG3G1Doe05s1vl-axIqHJwFDmhKtyhASMW8rk_AlrGPpzqTExtKG1NqhTcqG6RFnEIJApGBlLKYRwbgl-BhrqwwmZyo4IB9Wt3NPcHWlJZprPbNyM/s1297/Captura%20de%20pantalla%202023-12-31%2012.56.24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="581" data-original-width="1297" height="143" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiD1g496RlKlJddsGjYj0D3IX9KHQka7Xw4CiaA2iAdGv3whv6dFaBTkGO6-UPdhSE1iQL7KWFrUJSkG3G1Doe05s1vl-axIqHJwFDmhKtyhASMW8rk_AlrGPpzqTExtKG1NqhTcqG6RFnEIJApGBlLKYRwbgl-BhrqwwmZyo4IB9Wt3NPcHWlJZprPbNyM/s320/Captura%20de%20pantalla%202023-12-31%2012.56.24.png" width="320" /></a></div><br /><div><br /></div><div>Nuevamente nos desplazamos la cantidad de bytes que nos diga "<i>CurrentSize</i>" y nos encontraremos en la posición que contiene la cantidad de campos por la cual está compuesto el bloque por lo que asignamos la variable "<i>CurrentText</i>" con el resultado de mandar a llamar a la función "BitConverter.ToUInt32()". Ahora que tenemos este dato, crearemos un arreglo del tipo "string" y reservaremos tantos espacios cómo "<i>NumberOfFields</i>" nos diga.</div><div>Para leer los campos, simplemente crearemos un bucle que haga mas o menos lo mismo que hicimos ahorita: leeremos el tamaño, leemos la cadena de texto, desplazamos el marcador posición y repetimos hasta que leamos todos los campos. </div><div>La función recibe un parámetro del tipo arreglo de "<i>byte</i>" y devuelve una un arreglo del tipo "<i>string</i>" que contiene todos los campos encontrados.</div><div><br /></div><div>La siguiente función es "<i>GetBlockType</i>". Esta función lo que hace es "convertir" el valor de un solo byte a su equivalente a la descripción del tipo de bloque. Para hacerlo, simplemente desplazamos los bits del byte tres lugares a la izquierda y luego a la derecha, hay que recordad que el primer byte contiene el marcador por si es el ultimo bloque, pero además el tipo de bloque por lo que podríamos encontrar algo cómo sigue:10000110 , si lo desplazamos tres lugares a la izquierda, tendríamos: 00110000, y si lo desplazamos tres lugares a la derecha tendríamos 00000110 o seis decimal que marcaria un bloque del tipo "PICTURE". La función recibe un valor del tipo "<i>byte</i>" que es el byte que se va a convertir y regresa el resultado del desplazamiento.</div><div><br /></div><div>La siguiente función es "<i>IsLastFrame</i>". Esta sirve para determinar si el bloque actual es el último (de acuerdo a la descripción), esta función es similar a la anterior, cómo queremos aislar solo el primer bit (de izquierda a derecha), simplemente desplazamos los bits siete lugares a la derecha y comprobamos, si el resultado del desplazamiento es uno, la función devuelve "true" en caso contrario devuelve false.</div><div><br /></div><div>Las siguiente función es "<i>GetBlockSize</i>()" + 1 sobrecarga. Esta función se encarga de obtener el tamaño de un bloque. la primera recibe un parámetro y es un arreglo del tipo "byte" que contiene los datos. Para hacerlo, simplemente invertimos los datos del arreglo y regresamos el valor de mandar a llamar a la función "BitConverter.ToUInt32()". La sobrecarga recibe dos parámetros mas, el primero, un valor del tipo "<i>int</i>" que sirve cómo marcador de posición de donde se empezará a leer la secuencia de bytes, esta también se diferencia de la otra porque esta espera una secuencia de bytes "completa" o donde el primer grupo de cuatro bytes no es la secuencia que esperamos, por lo que es importante saber "donde empezar a leer", el tercer parámetro sirve para indica la longitud de bytes a leer. Ambas funciones devuelven un valor del tipo "<i>uint</i>" que representa el tamaño del bloque.</div><div><br /></div><div>El último procedimiento es "<i>GetBlock</i>()" este sirve para ejecutar la funciones encargadas de buscar los bloques. recibe dos parámetros, el primero un valor del tipo "<i>string</i>" que es la ruta del archivo del cual se quiere obtener información. El segundo es un valor de la enumeración "<i>BlockTypes</i>" que sirve para indicar el tipo de bloque que se va a buscar. Esta función fue la primera que hice y al principio solo la hice para buscar la secuencia "<i>fLaC</i>" por lo que puede parecer repetitiva, pero igual lo deje para dar mayor claridad a lo hicimos.</div><div><br /></div><div>Y bien, por ahora es todo,<a href="https://www.dropbox.com/scl/fi/x13g5296p7jpqopntsedv/FLACTagReader.zip?rlkey=38aqcvljdo9tq8hdqtv8u8gfw&dl=1" target="_blank"> cómo de costumbre puedes descargar el proyecto completo de mi dropbox para que lo pruebes por ti mismo</a>. Cabe resaltar que al igual cómo paso con los archivos MP3, aun falta mucho, pero algo que si haré en el corto plazo será crear un programa que agregue la caratulas de los discos.</div><div><br /></div><div>Los leo luego.</div><div><br /></div><p></p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-34395153261294298392023-12-17T12:57:00.002-06:002023-12-17T12:57:50.967-06:00Vamos a programar #103 - Leyendo metadatos de archivos FLAC (pt 2. Ver c#)<p>Hola de nuevo a todos. El día de hoy vamos a continuar con mas de los archivos FLAC y sus metadatos.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTTIv9yWHW9FBjTUK9TvriCDvE8M5Y4aQBUpOs1HINQRAOLXc0pfOyYSTeF2aQyJNB1n-zh9LEwFJEbLNF_ujhyaMxovFE3s26hwLos8OvRRRygw3MY4-ix7dI2uauIGy6sqLhGT1oS5R63irxnQiaBBZFfLyzFXpkRbZ2oheNAyACfn9PbhVMGSE_Eh_N/s1159/Captura%20de%20pantalla%202023-12-17%2009.52.12.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="809" data-original-width="1159" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTTIv9yWHW9FBjTUK9TvriCDvE8M5Y4aQBUpOs1HINQRAOLXc0pfOyYSTeF2aQyJNB1n-zh9LEwFJEbLNF_ujhyaMxovFE3s26hwLos8OvRRRygw3MY4-ix7dI2uauIGy6sqLhGT1oS5R63irxnQiaBBZFfLyzFXpkRbZ2oheNAyACfn9PbhVMGSE_Eh_N/s320/Captura%20de%20pantalla%202023-12-17%2009.52.12.png" width="320" /></a></div>En el post anterior vimos cómo es que está estructurado cada bloque de metadatos, incluso vimos cómo es que se hace la lectura de cada uno (y leímos el bloque "<i>STREAMINFO</i>"). Ahora que sabemos cómo, solo nos queda automatizarlo y para eso vamos a usar el lenguaje de programación C#.<div>Para empezar, veamos el código (sin ningún orden particular en las funciones):<div><br /><pre><br/>using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FLACTagReader
{
	public partial class Form1 : Form
	{
		// 0 : STREAMINFO 0000000
		// 1 : PADDING 0000001
		// 2 : APPLICATION 0000010
		// 3 : SEEKTABLE 0000011
		// 4 : VORBIS_COMMENT 0000100
		// 5 : CUESHEET 0000101
		// 6 : PICTURE 0000110
		// 7-126 : reserved 1111000 - Invalid
		// 127 : invalid, to avoid confusion with a frame sync code
		/// <summary>
		/// Enumeración con los posibles bloques contenidos en un archivo FLAC
		/// </summary>
		private enum BlocksTypes
		{
			/// <summary>
			/// Bloque mandatorio con la información del Stream
			/// </summary>
			Block_Type_StreamInfo = 0,
			/// <summary>
			/// Bloque de Padding
			/// </summary>
			Block_Type_Padding = 1,
			/// <summary>
			/// Bloque con la informacion de la aplicación
			/// </summary>
			Block_Type_Application = 2,
			/// <summary>
			/// Bloque Seektable
			/// </summary>
			Block_Type_SeekTable = 3,
			/// <summary>
			/// Bloque con metadatos
			/// </summary>
			Block_Type_VorbisComment = 4,
			/// <summary>
			/// Bloque con cuesheet
			/// </summary>
			Block_Type_CueSheet = 5,
			/// <summary>
			/// Bloque con imagen
			/// </summary>
			Block_Type_Picture = 6,
			/// <summary>
			/// Bloque no válido
			/// </summary>
			Block_Type_NoValid = 7
		}
		public Form1()
		{
			InitializeComponent();
		}
		/// <summary>
		/// Crea una imagen a partir de una secuencia de bytes
		/// </summary>
		/// <param name="bytesArr">Matriz de bytes que contiene los datos de la imagen</param>
		/// <returns>Regresa una imagen</returns>
		public Image ByteArrayToImage(byte[] bytesArr)
		{
			using (MemoryStream memstr = new MemoryStream(bytesArr))
			{
				Image img = Image.FromStream(memstr);
				return img;
			}
		}
		// 4 4 n*4 4 n*4 4 4 4 4 !4 !n*4
		//<32>,<32>,<n*8>,<32>,<n*8>,<32>,<32>,<32>,<32>,<32>,<n*8>
		// 1 2 3 4 5 6 7 8 9 10 11
		/// <summary>
		/// Lee todo los datos del bloque PICTURE y extrae la imagen contenida
		/// </summary>
		/// <param name="TheData">Arreglos bytes que contiene todo el bloque PICTURE</param>
		/// <returns>Regresa una imagen</returns>
		private Image ReadPictureData(byte[] TheData)
		{
			uint CurrentPos = 4;//1
			uint CurrentSize = GetBlockSize(TheData, (int)CurrentPos, 4);
			CurrentPos += 4;//2
			string MimeType = Encoding.ASCII.GetString(TheData, (int)CurrentPos, (int)CurrentSize);
			CurrentPos += CurrentSize;//3
			CurrentSize = GetBlockSize(TheData, (int)CurrentPos, 4);
			CurrentPos += (CurrentSize + (4 * 5));//4-9
			CurrentSize = GetBlockSize(TheData, (int)CurrentPos, 4);
			CurrentPos += 4;
			byte[] TempImage = new byte[CurrentSize];
			Array.Copy(TheData, CurrentPos, TempImage, 0, CurrentSize);
		 return ByteArrayToImage(TempImage);
		}
		/// <summary>
		/// Convierte un número en su valor equivalente al tipo de bloque
		/// </summary>
		/// <param name="Data">
		/// Entero sin signo con la representacion del bloque</param>
		/// <returns>Regresa un valor de la enumeración <typeparamref name="BlockTypes"/>BlockTypes</returns>
		private BlocksTypes NumberToBlockType(uint Data)
		{
			switch (Data)
			{
				case 0:
					return BlocksTypes.Block_Type_StreamInfo;
				case 1:
					return BlocksTypes.Block_Type_Padding;
				case 2:
					return BlocksTypes.Block_Type_Application;
				case 3:
					return BlocksTypes.Block_Type_SeekTable;
				case 4:
					return BlocksTypes.Block_Type_VorbisComment;
				case 5:
					return BlocksTypes.Block_Type_CueSheet;
				case 6:
					return BlocksTypes.Block_Type_Picture;
				default:
					return BlocksTypes.Block_Type_NoValid;
			}
		}
		/// <summary>
		/// Lee el bloque con la informacion estructurada
		/// </summary>
		/// <param name="TheData">Arreglo de bytes del cual se extraera la información</param>
		/// <returns>Regresa un arreglo del tipo string con todos los campos que se encontraron</returns>
		private string[] ReadVorbisData(byte[] TheData)
		{
			uint NumberOfFields = 0;
			uint CurrentField = 1;
			uint CurrentPosition = 0;
			//Vendor
			uint CurrentSize = BitConverter.ToUInt32(TheData, (int)CurrentPosition);
			CurrentPosition += 4;
			byte[] CurrentChunk = new byte[CurrentSize];
			Array.Copy(TheData, CurrentPosition, CurrentChunk, 0, CurrentSize);
			string Currenttext = Encoding.UTF8.GetString(CurrentChunk);
			CurrentPosition += CurrentSize;
			NumberOfFields = BitConverter.ToUInt32(TheData, (int)CurrentPosition);
			CurrentPosition += 4;
			string[] Fields = new string[NumberOfFields];
			//CommentField
			while (CurrentField <= NumberOfFields)
			{
				CurrentSize = BitConverter.ToUInt32(TheData, (int)CurrentPosition);
				CurrentPosition += 4;
				byte[] CurrenUserCommentList = new byte[CurrentSize];
				Array.Copy(TheData, CurrentPosition, CurrenUserCommentList, 0, CurrentSize);
				Currenttext = Encoding.UTF8.GetString(CurrenUserCommentList);
				CurrentPosition += CurrentSize;
				Fields[CurrentField - 1] = Currenttext;
				CurrentField += 1;
			}
			return Fields;
		}
	
		/// <summary>
		/// Obtiene el valor del bloque desde un byte
		/// </summary>
		/// <param name="Data">byte del cual se va a obtener la información</param>
		/// <returns>Regresa un valor que es equivalente al tipo de bloque</returns>
		private UInt32 GetBlockType(byte Data)
		{
			Data <<= 3;
			Data >>= 3;
			return Data;
		}
		/// <summary>
		/// Obtiene si el bloque es el último de la serie
		/// </summary>
		/// <param name="Data">byte del cual se obtendra la información</param>
		/// <returns>true si el bloque es el último, false en caso contrario</returns>
		private bool IsLastFrame(byte Data)
		{
			Data >>= 7;
			if (Data == 1)
				return true;
			else
				return false;
		}
		/// <summary>
		/// Obtiene el tamaño de un bloque
		/// </summary>
		/// <param name="Data">Arreglo de bytes que contiene el tamaño del bloque</param>
		/// <param name="StartIndex">Indica la posicion en donde se empezará a leer</param>
		/// <param name="Size">Indica el tamaño en bytes que se van a leer</param>
		/// <returns>Regresa el tamaño del bloque actual</returns>
		private UInt32 GetBlockSize(byte[] Data, int StartIndex, int Size)
		{
				byte[] CurrentData = new byte[4];
				Array.Copy(Data, StartIndex, CurrentData, 0, Size);
				Array.Reverse(CurrentData);
				return BitConverter.ToUInt32(CurrentData, 0);
		}
		/// <summary>
		/// Obtiene el tamaño de un bloque
		/// </summary>
		/// <param name="Data">Arreglo de bytes que contiene el tamaño del bloque</param>
		/// <returns>Regresa el tamaño del bloque actual</returns>
		private UInt32 GetBlockSize(byte[] Data)
		{
				byte[] CurrentData = Data;
				//Ponemos el primer byte en 0 porque se usa para identificar el bloque
				//a la hora de convertir a UInt32 se esperan 4 bytes, pero este siempre será 0 u otro valor no relevante
				//para el tamaño del bloque
				CurrentData[0] = 0;
				Array.Reverse(CurrentData);
				return BitConverter.ToUInt32(CurrentData, 0);
			
		}
		/// <summary>
		/// Obtiene los datos de un bloque
		/// </summary>
		/// <param name="FileName">Nombre del archivo del cual se obtendra</param>
		/// <param name="TypeOfBlock">Tipo de bloque que se va a buscar</param>
		private void GetBlock(string FileName, BlocksTypes TypeOfBlock)
		{
			byte[] DataBuff;
			bool LastFrame = false;
			FileStream FS = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
			using (BinaryReader BR = new BinaryReader(FS, Encoding.ASCII))
			{
				DataBuff = BR.ReadBytes(4);
				if (string.Equals(Encoding.ASCII.GetString(DataBuff), "fLaC", StringComparison.Ordinal))
				{
					UInt32 CurrentBlockSize;
					UInt32 CurrentPosition = (UInt32)FS.Position;
					BlocksTypes CurrentBlockType = BlocksTypes.Block_Type_NoValid;
					while (!LastFrame)
					{
						//primer bloque siempre será Block_Type_StreamInfo
						DataBuff = BR.ReadBytes(4);
						LastFrame = IsLastFrame(DataBuff[0]);
						CurrentBlockType = NumberToBlockType(GetBlockType(DataBuff[0]));
						if (CurrentBlockType == TypeOfBlock)
						{
							if (TypeOfBlock == BlocksTypes.Block_Type_VorbisComment)
							{
								string[] Fields;
								CurrentBlockSize = GetBlockSize(DataBuff);
								Fields = ReadVorbisData(BR.ReadBytes((int)CurrentBlockSize));
								textBox1.Clear();
								for (int i = 0; i < Fields.Length; i++)
								{
									textBox1.Text += Fields[i] + " | ";
								}
							}
							else if (TypeOfBlock == BlocksTypes.Block_Type_Picture)
							{
								CurrentBlockSize = GetBlockSize(DataBuff);
								pictureBox1.Image = ReadPictureData(BR.ReadBytes((int)CurrentBlockSize));
							}
						}
						else
						{
							CurrentBlockSize = GetBlockSize(DataBuff);
							FS.Seek(FS.Position + CurrentBlockSize, SeekOrigin.Begin);
						}
					}
				}
				else
				{
					MessageBox.Show("Archivo Flac no válido");
				}
			}
		}
		private void button1_Click(object sender, EventArgs e)
		{
			using (OpenFileDialog OpDiag = new OpenFileDialog())
			{
				OpDiag.Filter = "Archivos FLAC|*.flac|Todos los arhivos|*.*";
				if (OpDiag.ShowDialog() == DialogResult.OK)
				{
					GetBlock(OpDiag.FileName, BlocksTypes.Block_Type_VorbisComment);
					GetBlock(OpDiag.FileName, BlocksTypes.Block_Type_Picture);
					pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
				}
			}
		}
	}
} <br /></pre></div><div>Lo primero que hacemos es crear una enumeración llamada "<i>BlockTypes</i>" que contiene las posibles enumeraciones para cualquier tipo de bloque. Si bien la especificación dice que los valores de 0 hasta 127 son validos (aunque no tienen uso todos, al menos al momento de escribir esto), decidí declarar cómo no validos a los que superan al 6 y por eso, después de ahí, su nombre es "<i>Block_Type_NoValid</i>". Se podrían usar solo números, pero me pareció mas fácil de identificar cada uno de esta forma.<br /></div></div><div><br /></div><div>La primera función es "<i>ByteArrayToImage</i>()" del tipo "<i>Image</i>". Esta función, cómo su nombre lo pudiera indicar, se encarga de convertir una secuencia de bytes en una imagen (si los datos son válidos), recibe cómo parámetro un arreglo del tipo "byte[]" que contiene la secuencia de bytes de la imagen. Regresa un valor del tipo "<i>Image</i>" que es la imagen dentro del archivo (si es que hay).</div><div><br /></div><div>La siguiente función es "<i>ReadPictureData</i>()". Esta función es la encargada de procesar los datos del bloque "PICTURE". Recibe cómo parámetro un arreglo del tipo "byte[]" que contiene la secuencia de bytes del bloque "PICTURE" completo. Para procesarlo, se siguen las siguientes:</div><div><ul style="text-align: left;"><li><32> El tipo de imagen de acuerdo a la descripción de la etiqueta ID3 "APIC".</li><li><32> El tamaño de la descripción del tipo de archivo o <i>MIME type</i>.</li><li><n*8> Cadena de texto con la descripción del tipo de archivo.</li><li><32> El tamaño de la descripción de la imagen.</li><li><n*8> La descripción de la imagen en UTF8</li><li><32> El ancho de la imagen en píxeles</li><li><32> El alto de la imagen en pixeles.</li><li><32> La profundidad del color de la imagen en bits por pixel.</li><li><32> Para las imágenes con índice de color, el número de colores usados, para el resto 0.</li><li><32> El tamaño de los datos de la imagen.</li><li><n*8> Los datos de la imagen.</li></ul><div>Para realizar la lectura de la imagen se asume que solo se pasan los datos del bloque completos, solo omitiendo el tamaño del bloque mismo. Y en este caso, cómo solo nos interesa obtener la imagen "tal cual" solo nos concentraremos en leer las ultimas dos secuencias, pero en el código lo hacemos paso a paso para poder apreciar mejor lo que esta ocurriendo y para simplificarlo, lo podemos apreciar mejor en el siguiente diagrama:</div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixZ2l9wA5xfvgB3jXBJWIV98WkPVQEupeldKQqRGq-e6htIXt7vNizZ0w8UBXS7j414EVxxgMVn9OiWGJsYBmfi_rW2xfG73mDhbMufFk7Ue-3ANfNkngkgWXlzNTQhlr7DGGwtIJvYKJ5oWp4JzDaeQmtsBhhOlHdPeLeT097kjmLQ0dAS5-gyL90lLHL/s1317/seq5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="165" data-original-width="1317" height="80" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixZ2l9wA5xfvgB3jXBJWIV98WkPVQEupeldKQqRGq-e6htIXt7vNizZ0w8UBXS7j414EVxxgMVn9OiWGJsYBmfi_rW2xfG73mDhbMufFk7Ue-3ANfNkngkgWXlzNTQhlr7DGGwtIJvYKJ5oWp4JzDaeQmtsBhhOlHdPeLeT097kjmLQ0dAS5-gyL90lLHL/w640-h80/seq5.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div><br /></div><div>Todo este bloque se representa en 11 partes (parte de abajo de la imagen), cada una de la partes ya tiene su tamaño en bits establecido (parte media), que es lo mismo en bytes (parte superior). Si bien hace un momento mencione que vamos a ignorar todo salvo la última y penúltima parte, no podemos ignorar del todo a los otros datos, sobre todo a los que son variables. Ahora vamos leyendo el código.</div><div>Primero creamos una variable del tipo "<i>uint</i>" llamada "<i>CurrentPos</i>", esta la vamos a ir usando para saber en que parte del bloque estamos, si observaste bien, te darás cuenta que esta inicializada con un valor de 4, al hacer esto, estaremos brincando la primer secuencia de 32 bits </div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim6aMdVSdVaTBD_WvrXrRNY7wB56fZ73eBxLcuUURfB6RBxT09b54n764GD566ciZxfIRpB1Uk50YStTxW-p2pN_bbb9R105H9KDqyhqTbfva6-f5hk1EvCVUrUoWP7k6TeYSqmptioyZl_8iWyhRUNgJGzCyhCto9ML1qVAwFhRfUBouDeFNVWN6ntZC1/s1317/seq6.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="165" data-original-width="1317" height="50" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim6aMdVSdVaTBD_WvrXrRNY7wB56fZ73eBxLcuUURfB6RBxT09b54n764GD566ciZxfIRpB1Uk50YStTxW-p2pN_bbb9R105H9KDqyhqTbfva6-f5hk1EvCVUrUoWP7k6TeYSqmptioyZl_8iWyhRUNgJGzCyhCto9ML1qVAwFhRfUBouDeFNVWN6ntZC1/w402-h50/seq6.png" width="402" /></a></div><br /><div>Seguido, creamos una variable llamada "<i>CurrentSize</i>" del tipo "<i>uint</i>" y la asignamos con el valor de la llamada a la función "<i>GetBlockSIze</i>()" (que veremos pronto), con esto tendremos el tamaño de la siguiente secuencia (3) por lo que podemos avanzar 4 bytes mas el tamaño que nos dio con la función anterior </div><div>.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuEnOsG9cQYpP3ePrVGhqoRc0OtK0zPlfbbKuksGa3cZqpzqX5KCUD0XMobV2iNXsO85XSYV4IP_HbGB9NuA5oaaLsdtpbE2mqiNph6rkr9G8DDGkKj8txTICtS9tUYUJTY6rNR1k6Bj_mPrzODuaY4nCwigLtBTSZ1Qs9D-iKLtAWcER4hvtBYUeanjxB/s1317/seq7.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="165" data-original-width="1317" height="50" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuEnOsG9cQYpP3ePrVGhqoRc0OtK0zPlfbbKuksGa3cZqpzqX5KCUD0XMobV2iNXsO85XSYV4IP_HbGB9NuA5oaaLsdtpbE2mqiNph6rkr9G8DDGkKj8txTICtS9tUYUJTY6rNR1k6Bj_mPrzODuaY4nCwigLtBTSZ1Qs9D-iKLtAWcER4hvtBYUeanjxB/w400-h50/seq7.png" width="400" /></a></div>Aunque C# nos da muchas facilidades, a la hora de tratar de hacer lo mismo en otro lenguaje las cosas se pueden complicar, este bloque es una cadena de texto con la descripción del tipo de archivo por lo que es importante tenerlo en cuenta, para eso creamos una variable llamada "<i>MimeType</i>" del tipo "<i>string</i>" y se le asigna el valor de la llamada a la función "<i>Encoding.ASCII.GetString</i>()", esta recibe tres parámetro, el primero, un arreglo del tipo "byte[]" con los datos de la cadena ASCII que esperamos obtener; el segundo es un valor del tipo "<i>int</i>" que sirve cómo indicador de que parte del arreglo es donde se va a empezar a leer; el tercero es un valor del tipo "<i>int</i>" que sirve para indicar cuantos bytes de la secuencia se van a leer. Al realizar la "conversión" de una secuencia de "<i>bytes</i>" a "<i>string"</i>, tendremos algo cómo lo de la imagen que sigue:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9DpTOsFuRmswe2ctoLHecdLfjbGjAfw5dsv8foPW1ACPx9G2mlMUk-FC1zvL73yfOV7to_vFJElopAZqHU2qyzzhwt-8Njq8dn22ns0ZbfZk61KfzUNAKyn1jpoO_wdE2jB3l-btweJ0O187f7Tz0koIT5yX0JG4n5mKHqKYOp7mvnluPy_m5l7vA5jqK/s967/Captura%20de%20pantalla%202023-12-17%2011.47.22.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="477" data-original-width="967" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9DpTOsFuRmswe2ctoLHecdLfjbGjAfw5dsv8foPW1ACPx9G2mlMUk-FC1zvL73yfOV7to_vFJElopAZqHU2qyzzhwt-8Njq8dn22ns0ZbfZk61KfzUNAKyn1jpoO_wdE2jB3l-btweJ0O187f7Tz0koIT5yX0JG4n5mKHqKYOp7mvnluPy_m5l7vA5jqK/w400-h198/Captura%20de%20pantalla%202023-12-17%2011.47.22.png" width="400" /></a></div><div><br /></div><div>Luego sumamos la posición actual y el tamaño de la secuencia anterior. Para las dos siguientes partes, repetimos lo mismo: obtenemos el tamaño del bloque, lo sumamos a la posición actual y avanzamos cuatro bytes mas.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihyDQyYyv5JQQApeJIEuwItYArBQP_0lT43VAhtxCkZd-EwqCkwQfnUoJpirrKXZ23Ct44UeUArU9N2fXshxZKFm6ccCfldRte6zkMsJkxIR1IW3qQLy4tL3GxCQp4XzbwiU1lm_96ymSl16HFB_NWPdm-7QP6JPn5EEiWr8t5ASiD3V0Q9V13eisWxRA0/s1317/seq8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="165" data-original-width="1317" height="50" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihyDQyYyv5JQQApeJIEuwItYArBQP_0lT43VAhtxCkZd-EwqCkwQfnUoJpirrKXZ23Ct44UeUArU9N2fXshxZKFm6ccCfldRte6zkMsJkxIR1IW3qQLy4tL3GxCQp4XzbwiU1lm_96ymSl16HFB_NWPdm-7QP6JPn5EEiWr8t5ASiD3V0Q9V13eisWxRA0/w400-h50/seq8.png" width="400" /></a></div><br /><div style="text-align: left;">Las siguientes 4 secuencias son iguales en tamaño, por lo que podemos brincar 4*4 (en el código es 4*5 porque brinque la secuencia anterior tambien).</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjoa4xUtjxik0NcvSklnieALr2RkZSWiTzeiLAEDQx2l-ZoR3WElh3zbs7XSFlYMHAa5AE4diPFUAsdEywiKp8wIQZl-DQbFsKPyT6TvZlL2fnR8trXY8lZMhyZyPJ1BDXcFQbH1tKodZ4eyKdbALbf4KiHF8R3o3SkPItF0MiXwUWgLOSC2GDSwyRNhkN/s1317/seq9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="165" data-original-width="1317" height="50" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjoa4xUtjxik0NcvSklnieALr2RkZSWiTzeiLAEDQx2l-ZoR3WElh3zbs7XSFlYMHAa5AE4diPFUAsdEywiKp8wIQZl-DQbFsKPyT6TvZlL2fnR8trXY8lZMhyZyPJ1BDXcFQbH1tKodZ4eyKdbALbf4KiHF8R3o3SkPItF0MiXwUWgLOSC2GDSwyRNhkN/w400-h50/seq9.png" width="400" /></a></div><br /><div style="text-align: left;">Una vez leído las partes anteriores, solo queda leer los datos de la imagen por lo que repetimos nuevamente: obtenemos el tamaño de los datos de la imagen, creamos una nueva variable del tipo "byte[]" llamada "<i>TempImage</i>" y al mismo tiempo le asignamos el tamaño de la secuencia actual, luego copiamos la porción del bloque al arreglo que acabamos de crear y devolvemos el resultado de mandar a llamar la función "<i>ByteArrayToImage</i>()".</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Y bien, por ahora es todo, en el siguiente post publicaré el proyecto completo y terminaremos de revisar las funciones que no vimos.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-86899213471727096982023-12-03T09:12:00.001-06:002023-12-06T21:09:56.274-06:00Vamos a programar #102 - Leyendo metadatos de archivos FLAC (pt.1)<p> Hola de nuevo a todos. El día de hoy vamos a continuar con más de los archivos FLAC.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLB9_23URe5p96NI4O8RMRw1DKcRbI3tc1zpv6Lva-exLIcfjy10Gw754E25HIQVGpQaSPeHzogsw7v5oXlWTqyB1T8pPeDKRYB8v2ab4uoGCibcpJrURl6eU6l2G3YpIlRAgF_C1395JzfENKFBKmEbIhR6NFJP1o2ArkBZTA0HMA80NK9HQEibAY-3cp/s1345/Captura%20de%20pantalla%202023-12-02%2008.44.09.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="924" data-original-width="1345" height="220" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLB9_23URe5p96NI4O8RMRw1DKcRbI3tc1zpv6Lva-exLIcfjy10Gw754E25HIQVGpQaSPeHzogsw7v5oXlWTqyB1T8pPeDKRYB8v2ab4uoGCibcpJrURl6eU6l2G3YpIlRAgF_C1395JzfENKFBKmEbIhR6NFJP1o2ArkBZTA0HMA80NK9HQEibAY-3cp/s320/Captura%20de%20pantalla%202023-12-02%2008.44.09.png" width="320" /></a></div><br /><p></p><p>En los post mas recientes hemos visto las cualidades de los archivos FLAC, pero además cómo crear los nuestros. Al igual que en MP3, existen metadatos embebidos en ellos, y sirven para describir algunos datos importantes tales cómo: el nombre de la canción, el artista, el nombre del álbum, el genero, etc.</p><p><br /></p><h3 style="text-align: left;">Estructura de un archivo FLAC</h3><p>La estructura de los archivos FLAC es totalmente diferente a la de un MP3 por lo que vale la pena revisarla por separado. Cada archivo FLAC consiste de cuatro partes principales:</p><p></p><ul style="text-align: left;"><li>Una cadena de cuatro bytes con la secuencia "fLaC" (así tal cual se ve).</li><li>Un bloque de metadatos con la información del "<i>stream</i>"</li><li>Cero o mas bloques de metadatos.</li><li>Uno o mas "<i>frames</i>" de audio.</li></ul><br /><p></p><blockquote><div>Para mas detalles se puede revisar <a href="https://xiph.org/flac/documentation_format_overview.html" target="_blank">la página de xiph.org</a>, donde esta la descripción completa para los archivos FLAC.</div></blockquote><p>De las cuatro partes que se mencionan anteriormente, nos concentraremos en la segunda y tercera (Técnicamente las dos entran en el mismo conjunto, solo que la segunda es obligatoria, mientras que las subsecuentes son opcionales, hablando solamente de los bloques de metadatos), por lo que revisaremos cómo es que está compuesta cada una.</p><p>Pero antes una representación de cómo está estructurado el archivo:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikAlFclGnHNlGOZt0AVEZzL448Q-oFpC3Yr8Kw4U0lXJ3MJpNihY-giTtOrKyUSbcvlMjV218-uuo3V4m_rIZCTlzjao1hYZbNECAPtLxtJwhyocnXCEfT-0EKcHReFRDMGaixbBnJigfwsCPUPr50Yp2J1MnukQzBM8u6GJ3gGVIYOHIsl_IjcYjkPSoa/s1920/Captura%20de%20pantalla%202023-12-02%2015.46.18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikAlFclGnHNlGOZt0AVEZzL448Q-oFpC3Yr8Kw4U0lXJ3MJpNihY-giTtOrKyUSbcvlMjV218-uuo3V4m_rIZCTlzjao1hYZbNECAPtLxtJwhyocnXCEfT-0EKcHReFRDMGaixbBnJigfwsCPUPr50Yp2J1MnukQzBM8u6GJ3gGVIYOHIsl_IjcYjkPSoa/w640-h360/Captura%20de%20pantalla%202023-12-02%2015.46.18.png" width="640" /></a></div><br /><div>Si observamos bien la imagen, veremos que hay dos partes centrales: "<i>STREAM</i>" y "<i>FRAME</i>", por ahora revisaremos la parte de "<i>STREAM</i>" que contiene los datos que nos interesan. </div><div><br /></div><h3 style="text-align: left;">Metadatos</h3><div>A partir de ahora nos referiremos a cada parte cómo "bloque". Del lado de METADATA, cada bloque estará constituido por un encabezado (HEADER) y el bloque de datos en si, el encabezado se encarga de describir cómo es que cada parte esta formada. Para poder leer cada bloque, deberemos de seguir los datos de este. Salvo que se indique lo contrario, cada valor N representará un solo bit (OJO: bit, no byte al menos que se indique lo contrario),<a href="https://xiph.org/flac/format.html" target="_blank"> al igual que en la documentación (que recomiendo encarecidamente leer dos veces)</a>, usaremos un número entre los corchetes cuadrados "<" y ">" El valor que se encuentre en medio dirá cuantos bits conforman esa parte. Habiendo aclarado lo anterior, veamos cómo se forma el encabezado de cada bloque:</div><div><ul style="text-align: left;"><li>STREAM</li><li>METADATA_BLOCK</li><li>METADATA_BLOCK_HEADER (Encabezado)</li><ul><li><1> Indicador de último bloque</li><li><7> Descriptor del tipo de bloque</li><li><24> Tamaño del bloque en bytes (BYTES!!!)</li></ul></ul>Para el indicador del último bloque se usa un solo bit, si es 0 indica que hay mas bloques a continuación, pero si es 1, indica que el bloque es el último y empiezan los "FRAMES" por lo que si solo estamos leyendo los metadatos, deberíamos de dejar de buscar.</div><div><br /></div><div>La secuencia de los siguientes 7 bits, indican que tipo de bloque es el que sigue a continuación, para ello hay una serie ya predefinida y dependiendo del valor que este nos diga, el bloque del tipo será. Actualmente solo hay siete tipos de bloque ya predefinidos y son los que siguen:</div><div><ul style="text-align: left;"><li>0: STREAMINFO</li><ul><li>Contiene información acerca de todo el archivo, este bloque es mandatorio y siempre será e primero de la secuencia después de la secuencia 0x66, 0x4c, 0x61, 0x43 (o de manera simple después de "<i>fLaC</i>" ).</li></ul><li>1: PADDING</li><ul><li>Este tipo de bloque sirve cómo reserva y el contenido no representará nada, normalmente se llena con 0x00. Cuando se crea el archivo sin los metadatos completos, en lugar des escribir un archivo nuevo con los bloques nuevos, simplemente se reemplaza este bloque conservando el archivo original y agilizando la escritura del mismo.</li></ul><li>2: APPLICATION</li><ul><li>Nombre de la aplicación que se uso para codificar el archivo, puede ser cualquiera que el usuario elija, aunque se recomienda el registro de la aplicación para obtener un ID único para cada quien.</li></ul><li>3: SEEKTABLE</li><ul><li>Sirve cómo marcador para desplazarse en el archivo a la hora de la reproducción, aunque se puede omitir, se recomienda incluirlo para agilizar la lectura.</li></ul><li>4: VORBIS_COMMENT</li><ul><li>Este bloque contiene la información relevante a los creadores del contenido multimedia y la descripción del mismo, se usa la especificación de "Vorbis comment"</li></ul><li>5: CUESHEET</li><ul><li>Contiene información que se puede usar en un CD</li></ul><li>6: PICTURE</li><ul><li>Imagen representativa del archivo, similar a la imagen embebida en un archivo MP3.</li></ul><li>7-126: Reservado</li><ul><li>Reservado por ahora, por lo que se podría decir que si un bloque tiene descriptor en este rango, no es valido. Se planea su uso futuro.</li></ul><li>127: No válido</li><ul><li>Bloque no valido, ya que la misma secuencia se usa cómo marcador para sincronización.</li></ul></ul>Por lo que por ahora solo nos serian relevantes los bloques con descriptores: 0, 4, 6, 7-126 y 127. Especialmente el que tenga descriptor 0, porque es mandatorio; 4, porque seria el equivalente a los TAGS en mp3; y 6, porque es la imagen del álbum (por lo general). Sabiendo los tipos de bloques que vamos a leer, vamos a concéntranos solo en ellos, por lo que primero revisaremos cómo es que está armado el bloque "<i>STREAMINFO</i>".</div><div><br /></div><h4 style="text-align: left;">Bloque "STREAMINFO"</h4><div>El bloque STREAMINFO, consta de la siguiente secuencia:</div><div><ul style="text-align: left;"><li><16> Tamaño mínimo del bloque.</li><li><16> Tamaño máximo del bloque.</li><li><24> Tamaño mínimo del "<i>FRAME</i>" en bytes.</li><li><24> Tamaño máximo del "<i>FRAME</i>" en bytes.</li><li><20> Frecuencia de sampleo.</li><li><3> Número de canales.</li><li><36> Total de muestras por segundo.</li><li><128> MD5 del audio.</li></ul>Ya que cada una de las secciones tiene un tamaño ya predefinido, podemos asegurar que toda la secuencia siempre tendrá el mismo tamaño y cómo no es necesario que sepamos el contenido de esta, bastará con desplazarnos al siguiente bloque. Antes de continuar, haremos el calculo a mano de la posición en la que empieza y en la que acaba, para eso usaremos XVI32 para ver el contenido de forma hexadecimal, y el <a href="https://docs.google.com/uc?export=download&id=1acErNafAkFdHEYU1OFGKwVNPCngelLH-" target="_blank">archivo de muestra</a> será el que usamos para comparar en el post de <a href="https://xworkforall.blogspot.com/2023/10/el-video-correcto-8.html" target="_blank">"FLAC vs WAV"</a></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNzIPxscVREd6X_kx0TuVK9ROlCdzEMGOJAEsONBMIlkC6QL3W2lKLqsX_lH_S6ymrmsUg0vk_577f-VJtkvs88jZx5liDi-_9zIX-MdhJ9HjFsR3d5zoRtmm3gkGj9KHRTIt5WJ0TAoK1XQ27h1qdwibr02weMNzSpWExqFG1traiPy1uAzeYqb0wHw82/s1391/Captura%20de%20pantalla%202023-12-03%2007.15.33.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="473" data-original-width="1391" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNzIPxscVREd6X_kx0TuVK9ROlCdzEMGOJAEsONBMIlkC6QL3W2lKLqsX_lH_S6ymrmsUg0vk_577f-VJtkvs88jZx5liDi-_9zIX-MdhJ9HjFsR3d5zoRtmm3gkGj9KHRTIt5WJ0TAoK1XQ27h1qdwibr02weMNzSpWExqFG1traiPy1uAzeYqb0wHw82/w400-h136/Captura%20de%20pantalla%202023-12-03%2007.15.33.png" width="400" /></a></div>Si leemos las cosas en orden, debemos de empezar por buscar la secuencia "<i>fLaC</i>" que serán los primeros 4 bytes.<div><br /></div><div>Ahora que sabemos que el primer bloque debe de ser el bloque <i>STREAMINFO</i>, nos vamos a concentrar en eso, si hacemos memoria, lo primero que debemos de buscar de cada bloque, es su encabezado que serán 32 bits (4 bytes) por lo que inmediatamente después de la secuencia "<i>fLaC</i>" los siguientes bytes corresponden al encabezado de nuestro bloque.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE-4_g7-0FgB7HnZ-9TZcoetVL9a8YibEKPYp9ZfH9rChAaTN_HybER3k7T35SWiY1mJ9-FKYAMP_mtkuOxKtFwCtVFvQH4jys3iCpFf2O6uRSohsRCYZlrdLPxLStgz3aGaLj6LL99v5EXAcsOT6zWeI0U6QrIkPHxJGuBz0ceJ7HxG9Dlmm3bSSARgLw/s1391/Captura%20de%20pantalla%202023-12-03%2007.31.00.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="473" data-original-width="1391" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE-4_g7-0FgB7HnZ-9TZcoetVL9a8YibEKPYp9ZfH9rChAaTN_HybER3k7T35SWiY1mJ9-FKYAMP_mtkuOxKtFwCtVFvQH4jys3iCpFf2O6uRSohsRCYZlrdLPxLStgz3aGaLj6LL99v5EXAcsOT6zWeI0U6QrIkPHxJGuBz0ceJ7HxG9Dlmm3bSSARgLw/w400-h136/Captura%20de%20pantalla%202023-12-03%2007.31.00.png" width="400" /></a></div><br /><div><div>En este caso, el encabezado está representado por la secuencia hexadecimal: 0x00, 0x00, 0x00 y 0x22. ¿Pero cómo la interpretamos? simple, la referencia dice que el primer bit es el indicador del último bloque, solo si es 1, para entender mejor, veamos el número en binario.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2koTz4vh8HST11Uq7vsJEJeVo5enkxSVbZ_34wRq2Gpp9AjmlAgpGEu_qPqv4eFU6biWjNcLej4ZOPJbRd6BjsP_x4BUPHsky0fFl_05f9vlBh9LpQcYHiwgV69anLDhxZsL7DEKHelaG6s2kHCHRMYa-f5qsSkEQjonG0fS-PtOvcPQ7-9ugAT2CjfDY/s1002/Seq1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="136" data-original-width="1002" height="43" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2koTz4vh8HST11Uq7vsJEJeVo5enkxSVbZ_34wRq2Gpp9AjmlAgpGEu_qPqv4eFU6biWjNcLej4ZOPJbRd6BjsP_x4BUPHsky0fFl_05f9vlBh9LpQcYHiwgV69anLDhxZsL7DEKHelaG6s2kHCHRMYa-f5qsSkEQjonG0fS-PtOvcPQ7-9ugAT2CjfDY/s320/Seq1.png" width="320" /></a></div><br /><div>Cómo iremos leyendo "de byte en bytes", el mínimo de bits a leer es 8, pero sim problema podemos aislar el primer bit de cada byte y determinar si es o no el último bloque, en este caso, cómo encontramos un 0, indica que hay mas bloques después de este.</div><div><br /></div><div>Los siguientes 7 bits indicaran el tipo de bloque y para el tipo de bloque debe de ser 0, antes veamos cómo son los números del uno al seis en binario (recuerda que en el blog tenemos <a href="https://xworkforall.blogspot.com/2017/04/back-to-basics-2.html" target="_blank">DecToAny </a>que sirve para convertir un número decimal a cualquier base hasta 25, puedes descargar la aplicación para <a href="https://xworkforall.blogspot.com/2017/05/vamos-prgramar-35.html" target="_blank">Windows</a>, <a href="https://xworkforall.blogspot.com/2017/05/vamos-programar-37.html" target="_blank">android </a>o usar<a href="https://xworkforall.blogspot.com/p/convertidor-de-decimal-otras-bases.html" target="_blank"> la versión web)</a>.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYiAzv3xkOF3sPkMFsVSxTJK4dZiHvzCabzX9nchkBdAd84s9n72JWm4VIh6DW3UOIkDiLYszFjHUoOUljFUfE4v1NNf91r_htnuKctUgWjMs_RluXtYB2-o8asvX87uCLX5pWitJ9Z7u4uWpMOF2IgtCuOrw2MtvUGdrPt6Zd6K0QzbILSvVNeJa26ZoX/s1560/Screenshot_20231203-075847.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1560" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYiAzv3xkOF3sPkMFsVSxTJK4dZiHvzCabzX9nchkBdAd84s9n72JWm4VIh6DW3UOIkDiLYszFjHUoOUljFUfE4v1NNf91r_htnuKctUgWjMs_RluXtYB2-o8asvX87uCLX5pWitJ9Z7u4uWpMOF2IgtCuOrw2MtvUGdrPt6Zd6K0QzbILSvVNeJa26ZoX/s320/Screenshot_20231203-075847.png" width="148" /></a></div><br /><div><br /></div><div>Pero si no quieres molestarte en revisar, aquí la numeración binaria del cero al seis, que son los valores validos para esta parte de la secuencia.</div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1iduP8LMTz_mvJaMyTvhLJDl4_HoEJpCScoeD9J59e-6o0NOgxdw4kCIxu4U2V3l6yqTkpMMBweY03A6ub1x6xfqLd6OiIpatAk4poT2i7Ji20KCFMmldJ2YpuX8-DMyCvrPw-aAMgb0iH-5CIUsNAOMiUhMrfrw-kYsCh51YXVZA6bGAATp8AIs02MEX/s409/seq2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="338" data-original-width="409" height="264" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1iduP8LMTz_mvJaMyTvhLJDl4_HoEJpCScoeD9J59e-6o0NOgxdw4kCIxu4U2V3l6yqTkpMMBweY03A6ub1x6xfqLd6OiIpatAk4poT2i7Ji20KCFMmldJ2YpuX8-DMyCvrPw-aAMgb0iH-5CIUsNAOMiUhMrfrw-kYsCh51YXVZA6bGAATp8AIs02MEX/s320/seq2.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div>Cuando leemos la información del bloque encontramos con 0x00 lo que es igual cero decimal o 0 en binario, lo que confirma que el bloque es el bloque STREAMINFO. Los restantes 24 bits indican cual es el tamaño del bloque. Al igual que los hicimos con el primer byte, los siguiente 3, se irán leyendo de uno por uno, pero juntos representan un solo número.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLCrrvpRbr-D3M-JMw3ZtE2fLV-24V4G9Jy-QYGE69waJIYW4PKEHTIZOeJHjDm3L1nOddEFANHIWPPcu3KVf8TJOpYlXPDH_ptw2YRlSKC5-iR1c_ISyh-QoFGVr8Qa4qQhXz8bMHK0SC6rQWw-IPrERE14OxuOXTvGH_8uR1afUC1vNjlOZGwketYIDx/s811/seq3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="78" data-original-width="811" height="39" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLCrrvpRbr-D3M-JMw3ZtE2fLV-24V4G9Jy-QYGE69waJIYW4PKEHTIZOeJHjDm3L1nOddEFANHIWPPcu3KVf8TJOpYlXPDH_ptw2YRlSKC5-iR1c_ISyh-QoFGVr8Qa4qQhXz8bMHK0SC6rQWw-IPrERE14OxuOXTvGH_8uR1afUC1vNjlOZGwketYIDx/w400-h39/seq3.png" width="400" /></a></div>Toda la secuencia anterior representa 34<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFGx_JIkb5ulyOMabO_7L_fLwVnWGGDRifz_ItMYvjMq13t5BuhUM84u5tnAgy7vIobT5E2MmPz9XfGgIf3dbuUvxrWMW1MJ6zRjdLksxw2pQSOf62tecuKkbikYrmcHKO6-ulcgsYgNLpGQIQW-c_Dsbis6bMViKyAWTJNV55jfvbdFrElw2GlooFj_Jt/s854/seq4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="123" data-original-width="854" height="58" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFGx_JIkb5ulyOMabO_7L_fLwVnWGGDRifz_ItMYvjMq13t5BuhUM84u5tnAgy7vIobT5E2MmPz9XfGgIf3dbuUvxrWMW1MJ6zRjdLksxw2pQSOf62tecuKkbikYrmcHKO6-ulcgsYgNLpGQIQW-c_Dsbis6bMViKyAWTJNV55jfvbdFrElw2GlooFj_Jt/w400-h58/seq4.png" width="400" /></a></div><br /><div>Si en XVI32 marcamos esos 34 bytes, tendríamos seleccionado todo el bloque.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8s92qkIFpdmvwsApvWp6mwjR93BKqBdhqIW6i-FIT1nnnIceV9Pc8fqpyopfSAI2p-7DlgoAxo9Vj5PVpL_Fx7RrcpIiu0ImcAKmXeYyDzulJ8wDj4N7OjjKDl5gARftq1wE0Mrvwk9Z08Y8E4cKi2zACIoJagtYPMcE8-i0zdkHE8WZlJFjVZQJZEhke/s1391/Captura%20de%20pantalla%202023-12-03%2008.46.41.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="473" data-original-width="1391" height="136" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8s92qkIFpdmvwsApvWp6mwjR93BKqBdhqIW6i-FIT1nnnIceV9Pc8fqpyopfSAI2p-7DlgoAxo9Vj5PVpL_Fx7RrcpIiu0ImcAKmXeYyDzulJ8wDj4N7OjjKDl5gARftq1wE0Mrvwk9Z08Y8E4cKi2zACIoJagtYPMcE8-i0zdkHE8WZlJFjVZQJZEhke/w400-h136/Captura%20de%20pantalla%202023-12-03%2008.46.41.png" width="400" /></a></div><br /><div>Y bien, por ahora es todo. para no hacer muy extenso el post, terminaremos por hoy, sirve que da tiempo para <a href="https://xiph.org/flac/format.html" target="_blank">leer toda la documentación del los archivos FLAC</a>.</div><div>Si bien este post se podría tomar cómo una mala copia de la documentación, no lo es y estoy seguro que a mas de uno le será de ayuda, pero además así es nuestro estilo de trabajo, primero nos ensuciamos las manos descuartizando el archivo con el lector hexadecimal y luego hacemos código que hace lo mismo pero más rápido. Ahora y diferente al caso de <a href="https://xworkforall.blogspot.com/2016/10/vamos-programar-18_27.html" target="_blank">ArtView</a>, trataré de hacer versiones para los distintos lenguajes de programación.</div><div><br /></div><div>Ya es diciembre por lo que publicaré un par de post antes de que acabe el año. Es cierto que la actividad del blog vino en picada, pero para todos aquellos que se quedaron o que regresaron, los post iran saliendo de manera regular.</div><div><br /></div><div>Los leo luego.</div><div><div><br /></div></div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-3374858512474124022023-11-12T16:02:00.001-06:002023-11-14T21:25:02.075-06:00El Video (AUDIO) Correcto #9 - Convirtiendo CD a FLAC<p> Hola de nuevo a todos, el día de hoy vamos a ver cómo extraer nuestros audios de CD's originales y convertirlos a FLAC. Al inicio pensé en hacer alugan clase de programa para hacerlo, pero cómo probablemente solo uno de cada mil personas va a convertir sus discos (incluso a estas alturas, habrá quienes no los conozcan), decidí solo mostrar cual es el método que uso actualmente.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaAdxfJ3jI7IPBXS_eTGItlHJffp3BX_b2ekcrBnwF6sD0zcCkM-j6yizZSVM5Ah1buPBx4ZuSdyOybadTpThrEDIphk-PDoR55ewFThBUvUMfxjIlOix3Izxs4JTjnd12uprIKZRGKqPElaoSliaUXlnxEBBggvdimwlexeRxJC3SHWytRmKwfrtfkr_p/s1680/Captura%20de%20pantalla%202023-11-05%2016.01.23.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="910" data-original-width="1680" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaAdxfJ3jI7IPBXS_eTGItlHJffp3BX_b2ekcrBnwF6sD0zcCkM-j6yizZSVM5Ah1buPBx4ZuSdyOybadTpThrEDIphk-PDoR55ewFThBUvUMfxjIlOix3Izxs4JTjnd12uprIKZRGKqPElaoSliaUXlnxEBBggvdimwlexeRxJC3SHWytRmKwfrtfkr_p/s320/Captura%20de%20pantalla%202023-11-05%2016.01.23.png" width="320" /></a></div><br /><p>Para poder hacer la copia de los discos, vamos a usar <a href="https://docs.google.com/uc?export=download&id=10LF8GJrkxErPgdNNEqLNDoaPGnCQOg4M" target="_blank">MediaGo de SONY</a>, aunque es un programa relativamente viejo, aun al día de hoy funciona y tiene la virtud de que las funciones que requieren de internet, aun funcionan. Para no adelantarnos, vamos a ir en orden y <a href="https://docs.google.com/uc?export=download&id=10LF8GJrkxErPgdNNEqLNDoaPGnCQOg4M" target="_blank">lo primero que haremos es descargar el instalador de Media Go</a>. Una vez lo descargamos, lo instalamos y lo ejecutamos.</p><p>Una vez que el programa está listo, simplemente insertaremos nuestro CD. De lado izquierdo aparecerá en una ficha llamada "CD de audio"; hacemos clic en ella y podremos ver el contenido del CD.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL97kl-5UyCwX5slx7C7hvyKHuC9as_MaHwmUkUQxkhHTwi8rXYi3nrMqNkNdtUiokZucp5AkqTcDsqCFhjwv8pB0aM8jbj_TAbMzRD9r6yJJrPO4RjeMOfvuTtq48GR3We0KPtAbn-fsENtvABbDemIPfZTI0bz4X44crrotQOJLgddwSAXn-Je3kskrv/s1039/Captura%20de%20pantalla%202023-11-12%2015.36.17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1039" data-original-width="967" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL97kl-5UyCwX5slx7C7hvyKHuC9as_MaHwmUkUQxkhHTwi8rXYi3nrMqNkNdtUiokZucp5AkqTcDsqCFhjwv8pB0aM8jbj_TAbMzRD9r6yJJrPO4RjeMOfvuTtq48GR3We0KPtAbn-fsENtvABbDemIPfZTI0bz4X44crrotQOJLgddwSAXn-Je3kskrv/s320/Captura%20de%20pantalla%202023-11-12%2015.36.17.png" width="298" /></a></div><br /><p>Antes de continuar, haremos clic en el menú "Herramientas>>Preferencias" y se abrirá un dialogo con los ajustes del programa.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzGhf0wM9mVAGrkp33nDvnD-KehKVA8XHLBF7oEf3GtYk1XqVXw0NW4vmYG7aVj4trrGsihXWunaMdQudKDng_c7wcCR7_8h5ZLz3CoGJoimmyOXoexpffWeKxnJBxtwXvaNXt5fH4G85UBizE1g2rCv4P4zp0mqQF2O9XFXtW2XDuMK6yVaO4aGaqAF49/s618/Captura%20de%20pantalla%202023-11-12%2015.39.46.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="470" data-original-width="618" height="243" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzGhf0wM9mVAGrkp33nDvnD-KehKVA8XHLBF7oEf3GtYk1XqVXw0NW4vmYG7aVj4trrGsihXWunaMdQudKDng_c7wcCR7_8h5ZLz3CoGJoimmyOXoexpffWeKxnJBxtwXvaNXt5fH4G85UBizE1g2rCv4P4zp0mqQF2O9XFXtW2XDuMK6yVaO4aGaqAF49/s320/Captura%20de%20pantalla%202023-11-12%2015.39.46.png" width="320" /></a></div><br /><p>Hacemos clic en "Importación de CD" y en la caja de selección de la opción "Usar pistas usando el formato" seleccionaremos la opción FLAC. Hacemos clic en aceptar y los ajustes estarán listos.</p><p>Para comenzar la copia, simplemente hacemos clic en el botón "Importar CD". La copia comenzará y al finalizar, podremos escuchar nuestro archivos.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZXbwaU1VaKeR5aTmjoY7nlsCPmjmc5KrwTikp4s9jDq7XXAYzg7PiCp0uTHOgV5cWO5cgPLtynLbCCvLwje0UXsu6HoSA8xAezJlTJ5cYGIOuiYnEXWRmkofb5Z1t0L8iHXv9laq74QkhNAH9NaBzYicOa2w_MZmQATxoGUGNEgGRnNlluWlbl9syOubk/s1039/Captura%20de%20pantalla%202023-11-12%2015.44.03.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1039" data-original-width="967" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZXbwaU1VaKeR5aTmjoY7nlsCPmjmc5KrwTikp4s9jDq7XXAYzg7PiCp0uTHOgV5cWO5cgPLtynLbCCvLwje0UXsu6HoSA8xAezJlTJ5cYGIOuiYnEXWRmkofb5Z1t0L8iHXv9laq74QkhNAH9NaBzYicOa2w_MZmQATxoGUGNEgGRnNlluWlbl9syOubk/s320/Captura%20de%20pantalla%202023-11-12%2015.44.03.png" width="298" /></a></div><br /><p>La razón principal para usar "Media Go" es simple: Ya tiene todo lo necesario; cuando importa los archivos, se conecta a la base de datos Gracenote y obtiene todos los datos que puedan ser relevantes a la hora de identificar una canción.</p><p>Y bien, por ahora es casi todo...</p><p>He hablado de las cosas que son necesarias para poder oír audio en buena calidad (y no hablo de esos "audiofilos mugorosos" que le rebanan los bordes al los CD's para mejorar la calidad del audio), ya mencione los audifonos, los archivos de audio y la última cosa que es importante mencionar; y eso es el dispositivo que se va a encargar de reproducir el archivo.</p><p>Muchos de los celulares, simplemente contienen la "placa de audio" mas genérica que se pude encontrar, aun en los dispositivos de gama media, la mayor parte de ellos tendrán dicha placa que solo es capaz de reproducir hasta 32000Hz (si bien nos va); aunque parezca redundante; si conectamos unos audífonos de calidad superior al puerto 3.5mm del teléfono y este solo pude decodificar hasta cierta calidad, estaremos desperdiciando el hardware. Identificar si un teléfono es capaz de reproducir audio en alta calidad no es sencillo, ya que muchas veces es un dato que no es relevante a la hora de vender (siempre se verá primero la RAM y el CPU) pero si es posible, buscar el logotipo "Hi res audio" ayuda.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA1b6lKxFRX1f1kSvl72BTjfBqbJJihJcyav5Zmjx1ZBI9WZk2ZorTRcD9cWSARJPqwkSldQTI-XQiE1VVvT2HjOCrn20QOd89XLZR16mNRAS5ofpjkCGUh4ax8bfCdFsOghsVjABInRwfcxYnKmKkQpsCxvCWQCUJW9npouTSBTlJjRK74eGwuFSmLIiH/s665/High-Resolution-Audio-Wireless-Logo-1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="665" data-original-width="538" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjA1b6lKxFRX1f1kSvl72BTjfBqbJJihJcyav5Zmjx1ZBI9WZk2ZorTRcD9cWSARJPqwkSldQTI-XQiE1VVvT2HjOCrn20QOd89XLZR16mNRAS5ofpjkCGUh4ax8bfCdFsOghsVjABInRwfcxYnKmKkQpsCxvCWQCUJW9npouTSBTlJjRK74eGwuFSmLIiH/s320/High-Resolution-Audio-Wireless-Logo-1.jpg" width="259" /></a></div><br /><p>Aun si el equipo no cuenta con el logotipo, hay otras formas de saber si lo es.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt0q47vTNLOHzpTFbXYbBWTYulp_kL_xmpd16cNn2Hz_F-xyCSxLH81fNSEeWR-Z0qfAV9Gla85NnrzvwCsUJdO8ga-K0BLmGN-sZJN5Tx16mFAZmWLFTy43h7YuxhyHwjzi7uHKnr8D0I_CcBmCE_ViLGDg6xo9iciEUzczfOqUKPshT0JiaN6QrOTIHf/s1650/1699731329643.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1650" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt0q47vTNLOHzpTFbXYbBWTYulp_kL_xmpd16cNn2Hz_F-xyCSxLH81fNSEeWR-Z0qfAV9Gla85NnrzvwCsUJdO8ga-K0BLmGN-sZJN5Tx16mFAZmWLFTy43h7YuxhyHwjzi7uHKnr8D0I_CcBmCE_ViLGDg6xo9iciEUzczfOqUKPshT0JiaN6QrOTIHf/s320/1699731329643.jpg" width="140" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">LDAC ayuda a transmitir audio en alta calidad por Bluetooth</td></tr></tbody></table><br /><p><br /></p><p>Y bien, por ahora es todo, en el siguiente post veremos cómo leer los metadatos de los archivos FLAC en C# y varios lenguajes mas.</p><p>Los leo luego.</p><p><br /></p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-82713778769265621942023-10-01T20:20:00.003-06:002023-10-01T20:32:27.352-06:00El Video (AUDIO) Correcto #8 - WAV VS FLAC<p> Hola de nuevo a todos, el día de hoy vamos a continuar con un poco más sobre el audio de buena calidad.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1pCriBKtRwSPP2N-T_mHrxyXMfl_-SN6mZ7xqAjzls_SHyH9GWealXvgE7Po_riVshQj6y4hbtFRTnn-9cmR6LRjVNu12YzRbpn99PvMEFNsX5OWzBMLBCRP-CvbYTKsE5E_WI98hc5m26ZUDltYmeM8qRFm6Bfnf8yagHrY7RUL5HK8nKhcL3SnSg4aw/s1920/Captura%20de%20pantalla%202023-10-01%2014.54.18.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1040" data-original-width="1920" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1pCriBKtRwSPP2N-T_mHrxyXMfl_-SN6mZ7xqAjzls_SHyH9GWealXvgE7Po_riVshQj6y4hbtFRTnn-9cmR6LRjVNu12YzRbpn99PvMEFNsX5OWzBMLBCRP-CvbYTKsE5E_WI98hc5m26ZUDltYmeM8qRFm6Bfnf8yagHrY7RUL5HK8nKhcL3SnSg4aw/s320/Captura%20de%20pantalla%202023-10-01%2014.54.18.png" width="320" /></a></div><br /><p><br /></p><p>En el post anterior vimos cómo es importante tener un par de audífonos de la calidad adecuada para poder disfrutar de la música cómo es debido. Pero, no solo se trata de los audífonos, hay otros elementos que son importantes a la hora de disfrutar del audio de buena calidad. </p><p>En este post nos concentraremos en el archivo de audio en si. En reiteradas ocasiones he dicho que los archivos FLAC ofrecen una calidad superior al MP3, <a href="https://xworkforall.blogspot.com/2018/01/el-video-correcto-4.html" target="_blank">incluso en alguno de los post anteriores hice el comparativo</a>, en <a href="https://twitter.com/Mpm88G" target="_blank">twitter (o ahora X)</a>, mucha gente dijo que los archivos FLAC si bien tienen una calidad mejor que el MP3, también se pierde mucha de la información que conforma la música, y esto es cierto o no, dependiendo de cómo se configura la compresión, cual es el origen del cual vamos a crear nuestro FLAC y algunos otros factores. Pero por lo general, si se configura bien, los archivos FLAC no tendrán ninguna perdida y para probarlo, vamos a hacer la misma prueba que hicimos hace tiempo; vamos a comparar FLAC contra WAV. ¿Por qué WAV? te estarás preguntando, simple, es el contenedor que se usa para el audio sin compresión, por lo que su tamaño será significativamente mayor al de los otros.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXutPQYWLCrjy1aYFopo6sZlVvAWeoHSwhss7BLND_h8Gga5BkqJyMJb6_4dGKPUMFmUCbkuaZvQrNtY00Z1NM9UAjx8Py8BdN5_KQjBir5DzBPN4vchhYSO6GSu5na2penSb49i47SNIQmQp846XgqgMb97l_JKydeMqca2q0r7_JV9rjqneMGoabTkJr/s516/Captura%20de%20pantalla%202023-10-01%2015.09.15.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="516" data-original-width="377" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXutPQYWLCrjy1aYFopo6sZlVvAWeoHSwhss7BLND_h8Gga5BkqJyMJb6_4dGKPUMFmUCbkuaZvQrNtY00Z1NM9UAjx8Py8BdN5_KQjBir5DzBPN4vchhYSO6GSu5na2penSb49i47SNIQmQp846XgqgMb97l_JKydeMqca2q0r7_JV9rjqneMGoabTkJr/s320/Captura%20de%20pantalla%202023-10-01%2015.09.15.png" width="234" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Archivo WAV copiado desde un CD ORIGINAL</td></tr></tbody></table><br /><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_sY3cqVb_-cLaAtkumFLVGANmwSBAZQirO24IvOYEYjbI-haa_nf8ZaB_XHBHBdPOYsVCnnhP-WcGZyP1AXo3uNYtmm26RCNv8XYrGtuBk3IHhHyiLIn7ZzQVEWpz7hrBV8JDzDfQ9AHTPqPfSlwYWkBM0IPpAny25Q_0FUVvOFHyjG3AQ3FiAzQQaIKD/s516/Captura%20de%20pantalla%202023-10-01%2015.11.11.png" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="516" data-original-width="377" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_sY3cqVb_-cLaAtkumFLVGANmwSBAZQirO24IvOYEYjbI-haa_nf8ZaB_XHBHBdPOYsVCnnhP-WcGZyP1AXo3uNYtmm26RCNv8XYrGtuBk3IHhHyiLIn7ZzQVEWpz7hrBV8JDzDfQ9AHTPqPfSlwYWkBM0IPpAny25Q_0FUVvOFHyjG3AQ3FiAzQQaIKD/s320/Captura%20de%20pantalla%202023-10-01%2015.11.11.png" width="234" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Archivo FLAC creado desde un CD ORIGINAL</td></tr></tbody></table><br /><h2 style="text-align: left;">Las pruebas.</h2><div>Al igual que la vez anterior, vamos a usar AUDACITY para comparara los audios. Su interfaz a cambiado un poco, pero si sigues los pasos que te mostraré, todo saldrá igual. En esta ocasión de igual forma subiré dos archivos de audio de la misma canción, pero eso si, recuerda que si no posees un CD original, debes de eliminarlo a las 24 horas máximo (resulta difícil tener una copia original, aun buscando el ID del CD que es: "Fantasía TFF-3838" no se encuentra, ni en la pagina de fonovisa que es la que lo editó).</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhY3bg2x2QmwLhWv3_mRntITfRc-XhaTNRpXMSZUvYkzZi3f5RiLVj6p44NSMlr5Ej6RnrJ66F6FlfzzZ5w4KZMK94dYyZWZow5kVSN8hISk4NTo42iwgmr_-mbJ4cQg23NimWbhDEPMMvosAE568-VvjhNBl2rHQcmKUlFyCsOEoe5Uz8kddWFAU_ZUVIB/s4000/IMG_20231001_152113.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhY3bg2x2QmwLhWv3_mRntITfRc-XhaTNRpXMSZUvYkzZi3f5RiLVj6p44NSMlr5Ej6RnrJ66F6FlfzzZ5w4KZMK94dYyZWZow5kVSN8hISk4NTo42iwgmr_-mbJ4cQg23NimWbhDEPMMvosAE568-VvjhNBl2rHQcmKUlFyCsOEoe5Uz8kddWFAU_ZUVIB/s320/IMG_20231001_152113.jpg" width="320" /></a></div><br /><div><br /></div><div><ol style="text-align: left;"><li><a href="https://www.audacityteam.org/download/" target="_blank">Descargar audacity.</a></li><li><a href="https://docs.google.com/uc?export=download&id=1_F1AzYeOdgaRhrgMANMLxQQmHqs_qIud">Descargar el archivo WAV.</a></li><li><a href="https://docs.google.com/uc?export=download&id=1acErNafAkFdHEYU1OFGKwVNPCngelLH-">Descargar el archivo FLAC.</a></li></ol>Una vez instalado audacity, lo abriremos luego arrastraremos los archivos de audio que descargamos,</div><div>puede ser uno o los dos a la vez. Una vez abiertos, seleccionaremos una pista de audio (de hecho dos porque son estéreo) e iremos al menú "Efectos>>Especial>>Invertir" con esto crearemos una versión invertida de nuestra canción. Para "sumarlas", bastara con seleccionar las dos pistas (técnicamente cuatro pistas porque ambas son estéreo) para hacerlo, haremos clic en el panel izquierdo y muestras mantenemos presionada la tecla "control" hacemos lo mismo con la otra. Una vez seleccionada las dos pistas, iremos al menú "Pistas>>Mezclar y generar nueva pista" y con esto se generará una nueva que se vera cómo una línea. En esta ocasión no es necesario alinear los inicios de las pistas puesto que eso ya lo hice (nuevamente puedes revisar el post donde comparé MP3 y FLAC).</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLbcuq09vPLpiat90TVh_0ChHKqrKepqzFtwL5w_WiEVNAJc0CLGvaeZ6OfNqwgpcDtroVpn665RjIYUHnND0fd4PT0DgFuYu-1d81ssE0AhxWy_77lPQZhv7xV8gSctYT1Cso9-k9WQjWR3NwEU-VjzFEKmobOWCxmOaaS9r22Sy0rrmFpaH_mHpNQP4n/s1920/Captura%20de%20pantalla%202023-10-01%2015.46.53.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1040" data-original-width="1920" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLbcuq09vPLpiat90TVh_0ChHKqrKepqzFtwL5w_WiEVNAJc0CLGvaeZ6OfNqwgpcDtroVpn665RjIYUHnND0fd4PT0DgFuYu-1d81ssE0AhxWy_77lPQZhv7xV8gSctYT1Cso9-k9WQjWR3NwEU-VjzFEKmobOWCxmOaaS9r22Sy0rrmFpaH_mHpNQP4n/s320/Captura%20de%20pantalla%202023-10-01%2015.46.53.png" width="320" /></a></div><div><br /></div><div>Cómo el archivo resultante es "silencio" podemos afirmar que ambas pistas son iguales, solo que, la ventaja del archivo FLAC es que pesa casi la mitad que el archivo WAV.</div><div><br /></div><div>Y bien, por ahora es todo, en el siguiente post ahora si veremos cómo importar nuestros discos al formato FLAC.</div><div><br /></div><div>Los leo luego.</div><div><br /></div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-55594281583837738312023-09-02T09:59:00.003-06:002023-09-02T10:02:04.267-06:00El Video (AUDIO) Correcto #7 - Un poco mas sobre el audio.<p> Hola de nuevo a todos, el día de hoy vamos a ver un poco mas sobre los archivos FLAC.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5TJKiqnkcQaL59O0STvYgovdEzCWRerWbnQboaSJdf6IdqRZEvtegsh_x53Ik7P_3m85Ln40uSFk0LvZqLeIlwJqNbEbqvG3qqZlF2799qKmc2zkH_hN9GCJL3IBtJY0CK3DrVy2EVgayUgHl-BKcLjNHtzn4uvnSSbEiZwmK2l0ILFPeSUJCypUu4ONu/s1200/Pioneer-SE-MS5T.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="665" data-original-width="1200" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5TJKiqnkcQaL59O0STvYgovdEzCWRerWbnQboaSJdf6IdqRZEvtegsh_x53Ik7P_3m85Ln40uSFk0LvZqLeIlwJqNbEbqvG3qqZlF2799qKmc2zkH_hN9GCJL3IBtJY0CK3DrVy2EVgayUgHl-BKcLjNHtzn4uvnSSbEiZwmK2l0ILFPeSUJCypUu4ONu/w400-h221/Pioneer-SE-MS5T.jpg" title="imagen tomada de https://www.revistagadgets.com/2017/04/04/audio-hi-res-la-nueva-linea-audifonos-pioneer/" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">imagen tomada de: <a href="https://www.revistagadgets.com/2017/04/04/audio-hi-res-la-nueva-linea-audifonos-pioneer/">https://www.revistagadgets.com/2017/04/04/audio-hi-res-la-nueva-linea-audifonos-pioneer/</a></td></tr></tbody></table><br /><p><br /></p><p>Cómo muchos de ustedes sabrán, me gusta mucho oír música (música de verdad y no "regeton"), pero siempre había usado equipo de "baja calidad" (y solo por nombrarlo de alguna manera). Pero no hace mucho tiempo, visité un lugar donde se exponían diferente sistemas de audio, desde los mas sencillos; cómo bocina bluetooth; hasta los mas complejos; llámese audio de estudio.</p><p><br /></p><p>Dentro del mismo lugar había un apartado para los audífonos, de inmediato fui a revisar, y había varios para probar. Cada uno tenia la ficha que describía cuales eran sus características, pero hacían un fuerte énfasis en la frecuencia de respuesta de cada uno.</p><p><br /></p><p>Primero probé los que ofrecían una respuesta de 20 hasta 20000Hz, justo cómo la mayor parte de audífonos son, la calidad era aceptable, mucho de los audífonos que he tenido cumplían con esa frecuencia y todo parecía aceptable. Los siguientes tenían una respuesta de 20 hasta 32000Hz, la misma calidad que los audífonos que siempre usaba, y justo cómo los anteriores, todo funcionaba bien, y a mi gusto, nada podía superar la calidad. En la siguiente mesa había unos audífonos con una respuesta de 20 hasta 40000Hz... había vivido engañado todo el tiempo, siempre me habían dicho que el oído humano no era capaz de oír frecuencias mas allá de los 32000Hz, pero al momento de probar esos, de inmediato me di cuenta que, si bien es cierto que el oído humano está limitado a ciertas frecuencias, el hecho es que cada persona es diferente y hay quienes alcanzan un rango auditivo mayor al de otros. En mi caso es muy difícil decir hasta "donde puedo escuchar", ya que nunca he hecho un estudio que sirva para eso, pero no había duda de que esos audífonos "sonaban mejor" que los otros, por lo que decidí conseguir unos.</p><p><br /></p><p>Finalmente cosegui unos <a href="https://www.edifier.com/global/p/over-ear-on-ear-headphones/w820nb-plus" target="_blank">audifonos edifier w820nb plus</a> que cuestan poco y tienen la calidad superior a los que ya tenía. </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2s7WoFhSjNzJ-d4BMeF7Rhmn5dkQ7zFqloVRgtkkWVAkGKt-qKd9sCFRT4aRmzG7SID3cZwQjAjNRTXo8jpemdbZ3FyGEHaAT62oH2CqQQt1o6PH9mNCy45L7sQI0okRF-187V2HxtN-7XRyeAJCqzXiq_nucvtQ5B2KZCNnMAQfNlK--VvaAuWYbOh4q/s1920/Captura%20de%20pantalla%202023-09-02%2009.30.10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1040" data-original-width="1920" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2s7WoFhSjNzJ-d4BMeF7Rhmn5dkQ7zFqloVRgtkkWVAkGKt-qKd9sCFRT4aRmzG7SID3cZwQjAjNRTXo8jpemdbZ3FyGEHaAT62oH2CqQQt1o6PH9mNCy45L7sQI0okRF-187V2HxtN-7XRyeAJCqzXiq_nucvtQ5B2KZCNnMAQfNlK--VvaAuWYbOh4q/w400-h216/Captura%20de%20pantalla%202023-09-02%2009.30.10.png" width="400" /></a></div>Pero para poder disfrutar de un buen audio, no solo basta con tener los audífonos, también hay que tener un dispositivo compatible y los archivos de audio adecuados. Por fortuna para mi, aun soy de la vieja escuela y tengo mi colección de discos guardados. Normalmente lo que hacia era utilizar mi reproductor de CD (si, en 2023 aun sigo usándolo), pero el inconveniente que encontré, es que la única salida que tiene, es el plug tradicional de 3.5 mm, mientras que los audífonos soportan bluetooth y USB-C.<div><br /></div><div>Para poder escuchar mis discos decidí crear una copia de ellos, pero no en mp3, porque ya en ocasiones anteriores he hablado de ciertas desventajas que este tiene con respecto a FLAC, <a href="https://xworkforall.blogspot.com/2018/01/el-video-correcto-4.html" target="_blank">detalles que puedes encontrar en un post que hice hace un buen rato.</a></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkvUPGJbqcqgMRXkUkoQVWLl9G6nZkKkvto1jpBOQ9ulCSWN0ysUicvwyIWq-2rbDYRnwmyHritmwFKsHG3BAXFv9M3GfOrzt1IuGiNgH1D8eO4eIXH6vdsIzzeBSy0bohDde4N1-_D4hF1tPL3YVOquC8Dpcu68LTC7QjJSKtFoJRc6dj77ubYiTnmLHH/s1034/Captura%20de%20pantalla%202023-09-02%2009.45.27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="745" data-original-width="1034" height="231" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkvUPGJbqcqgMRXkUkoQVWLl9G6nZkKkvto1jpBOQ9ulCSWN0ysUicvwyIWq-2rbDYRnwmyHritmwFKsHG3BAXFv9M3GfOrzt1IuGiNgH1D8eO4eIXH6vdsIzzeBSy0bohDde4N1-_D4hF1tPL3YVOquC8Dpcu68LTC7QjJSKtFoJRc6dj77ubYiTnmLHH/s320/Captura%20de%20pantalla%202023-09-02%2009.45.27.png" width="320" /></a></div><br /><div><br /></div><div><br /></div><div> Y bien, por ahora es todo, en los siguientes post veremos detalles de cómo importar la música en FLAC, pero además entraremos en detalles técnicos para poder leer y editar los metadatos de los archivos; para mantener todo mas o menos organizados (<a href="https://xworkforall.blogspot.com/2016/09/vamos-programar-15.html" target="_blank">al igual que lo hicimos con MP3</a>).</div><div><br /></div><div>Los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-71918992383156356322023-06-18T21:46:00.002-06:002023-06-18T21:46:26.745-06:00Cumplimos 7 años -_-!!!! <p> </p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6HXH1ZAzg0WZtRiePQG4HWoQe5Z02BO44d6RFAT5WpJSK_5uxpUUyBfA7CfG9rcx3FblBPqhL7UNb5w3xIuWsmvLEbOiksC7jhqroZNfEnK1uTIMxBICuJSOPhodPLVCR5RWiphDMGBcFyxhbExzveIrlmQXvp0xQ5PWldaWE9msymoNnDFJrA7oRAg/s1920/BlogX6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6HXH1ZAzg0WZtRiePQG4HWoQe5Z02BO44d6RFAT5WpJSK_5uxpUUyBfA7CfG9rcx3FblBPqhL7UNb5w3xIuWsmvLEbOiksC7jhqroZNfEnK1uTIMxBICuJSOPhodPLVCR5RWiphDMGBcFyxhbExzveIrlmQXvp0xQ5PWldaWE9msymoNnDFJrA7oRAg/w400-h225/BlogX6.jpg" width="400" /></a></div>Hola de nuevo a todos, el dia de hoy solo escribo para recordar que un dia cómo hoy pero de hace siete años, nació el blog de Xwork. Si bien es que ha habido altas y bajas; sobre todo bajas hablando del blog; solo quiero decir que ya estoy trabajando para continuar con el plan de un post al mes.<p></p><p>En tiempos recientes, he tenido que atender otros asuntos y contrario a lo que se podria pensar, basados en el estado del blog; realmente estoy disfrutando el momento y por eso es que lo he descuidado, pero cómo todo, estoy tratando de encontrar el balance entre haer lo que debo y lo que me gusta (que casí van de la mano).</p><p>Por eso nuevamente quiero agradecer a todos los que visitan y recordarles que sigan al pendiente que esto seguirá mientras no haya ningun impedimento.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9Pp3UNXNme3LpEX4P6FZg_IT_w_1gbpwNSc3CgrvoXJvlTthRJ1ac52U0DXzqcJePKefaP07mT9eatXLHQla-FSwbru8F77V5YQBMgsyb2aInvcTxXvhFwv4jJ9TQFrJIFa_bdC7m56Zd8yufqZ9Ii9dtsZEbkYfTn7vcSIxZYFiIDHyN5xh-K5xB_A/s4000/IMG_20230618_214113.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9Pp3UNXNme3LpEX4P6FZg_IT_w_1gbpwNSc3CgrvoXJvlTthRJ1ac52U0DXzqcJePKefaP07mT9eatXLHQla-FSwbru8F77V5YQBMgsyb2aInvcTxXvhFwv4jJ9TQFrJIFa_bdC7m56Zd8yufqZ9Ii9dtsZEbkYfTn7vcSIxZYFiIDHyN5xh-K5xB_A/s320/IMG_20230618_214113.jpg" width="320" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Hay varios proyectos listos, solo hay que esperar un poco</td></tr></tbody></table><br />Saludos y nos seguimos leyendo.<br /><p><br /></p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-32804643759092754832023-03-19T18:44:00.001-06:002023-03-19T18:44:27.162-06:00Vamos a programar #101 - Operaciones a nivel de bits en Python y tablas de verdad.<p> Hola de nuevo a todos, el día de hoy vamos a ver mas operaciones a nivel de bits.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY9cTCwdg2QfIhxHifmTF9MkLkZXIBfoFf0tZz7fNpV86dfrrsa6CIamu5gkTGHjS0aZYSqR2gcSXh9hU7UnpjOxvR8ZqY_rm2jt7ijBqq8EhioQx7EU7Iuw42K9_F6tFni_b95_GGUYM7WUNXmUU1vjDybBhuAzNjkK8zLN059giHta4sBPUBvLDraQ/s1280/ShiftToRight.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1280" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY9cTCwdg2QfIhxHifmTF9MkLkZXIBfoFf0tZz7fNpV86dfrrsa6CIamu5gkTGHjS0aZYSqR2gcSXh9hU7UnpjOxvR8ZqY_rm2jt7ijBqq8EhioQx7EU7Iuw42K9_F6tFni_b95_GGUYM7WUNXmUU1vjDybBhuAzNjkK8zLN059giHta4sBPUBvLDraQ/w400-h225/ShiftToRight.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><p>Hace algunos días alguien me pregunto que si era posible usar operaciones a nivel de bits en Python, la respuesta sencilla es: si. Pero ¿Cómo es que trabaja cada una y cuales son las que están disponibles? Anteriormente vimos cómo es posible usarlos en C# y si bien son esencialmente lo mismo, vale la pena revisarlas por separado. En este post veremos las operaciones disponibles SOLO para Python.</p><p><br /></p><p>En Python disponemos de las siguientes operaciones:</p><p></p><ul style="text-align: left;"><li>Or (o) "|"</li><li>XOr (o exclusivo) "^"</li><li>And (y) "&"</li><li>Shift left (desplazamiento a la izquierda) "<<"</li><li>Shift right (desplazamiento a la derecha) ">>"</li><li>Not (inverso) "~"</li></ul><br /><h2 style="text-align: left;">Or "|"</h2><div>El operador "|" aplica la operación O entre dos números, hay que recordar las tablas de verdad y recordar que que se produce un valor verdadero cuando uno u otro de los valores son verdaderos.</div><div><br /></div><p></p><p></p><ul style="text-align: left;"><li>Verdadero O Verdadero = Verdadero</li><li>Verdadero O Falso = Verdadero</li><li>Falso O Verdadero = Verdadero</li><li>Falso O Falso = Falso</li></ul>Que es lo mismo:<p></p><p></p><ul style="text-align: left;"><li>1|1 = 1</li><li>1|0 = 1</li><li>0|1 = 1</li><li>0|0 = 0</li></ul>Si escribimos lo anterior en la consola de Python, comprobamos que es correcto.<p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibKtBPM0uRaiEdztl0VdKh702xdRsAIaIB056uXcy41nI_ZRyy6ESEEOdrO6Gs0if9iyHN2yS9tvQ52qjdtFREbeGBZ12Jw5Rs0bmftvCyN3lisfQWTSCYh-eXRGKKs0tsta3y39h5T3DGMLokypY2O7TgkWCTLTa14TSkJfrLO5OJD5l04LGhNiR1Kg/s993/Captura%20de%20pantalla%202023-03-19%2016.56.29.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="519" data-original-width="993" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibKtBPM0uRaiEdztl0VdKh702xdRsAIaIB056uXcy41nI_ZRyy6ESEEOdrO6Gs0if9iyHN2yS9tvQ52qjdtFREbeGBZ12Jw5Rs0bmftvCyN3lisfQWTSCYh-eXRGKKs0tsta3y39h5T3DGMLokypY2O7TgkWCTLTa14TSkJfrLO5OJD5l04LGhNiR1Kg/w400-h209/Captura%20de%20pantalla%202023-03-19%2016.56.29.png" width="400" /></a></div><br /><h2 style="text-align: left;">XOr "^"</h2><div>El operador "^" aplica la operación O Exclusiva a dos números, de nueva cuenta, tomado las tablas de verdad tenemos:</div><div><ul><li>Verdadero OEx Verdadero = Falso</li><li>Verdadero OEx Falso = Verdadero</li><li>Falso OEx Verdadero = Verdadero</li><li>Falso OEx Falso = Falso</li></ul></div><div>Que es lo mismo:</div><div><ul><li>1^1 = 0</li><li>1^0 = 1</li><li>0^1 = 1</li><li>0^0 = 0</li></ul><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCb2mcNCQK1wXgk8qxt6u-lZPsGNKJPQ3FqrZ9jeY_nxfCSBNdllKJaReSfgMLc9c0ENAZYKSt7_UtDAYv4Z705wCl1-7-x7svV6E7OnCT0mm0Gjk5_TYMm6Hnbl9GDNFs_gULRxohJaQtF8rC0NMYCUGHc1CXxHUq69UGx8XNyoH4uLEyFaQruiHTNA/s993/Captura%20de%20pantalla%202023-03-19%2017.16.30.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="519" data-original-width="993" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCb2mcNCQK1wXgk8qxt6u-lZPsGNKJPQ3FqrZ9jeY_nxfCSBNdllKJaReSfgMLc9c0ENAZYKSt7_UtDAYv4Z705wCl1-7-x7svV6E7OnCT0mm0Gjk5_TYMm6Hnbl9GDNFs_gULRxohJaQtF8rC0NMYCUGHc1CXxHUq69UGx8XNyoH4uLEyFaQruiHTNA/w400-h209/Captura%20de%20pantalla%202023-03-19%2017.16.30.png" width="400" /></a></div><br /><h2 style="text-align: left;">And "&"</h2><div>El operador "&" aplica la operación Y a dos números y tiene una tabla de verdad como la que sigue:</div><div><div><ul><li>Verdadero Y Verdadero = Verdadero</li><li>Verdadero Y Falso = Falso</li><li>Falso Y Verdadero = Falso</li><li>Falso Y Falso = Falso</li></ul></div><div>Que es lo mismo:</div><div><ul><li>1&1 = 1</li><li>1&0 = 0</li><li>0&1 = 0</li><li>0&0 = 0</li></ul><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJTlKrSWff1tOWJE3UCt3IacBuoZdUc6AKDyh-tRvC3T_eR8Q0SSdCFie_LRBRb4BI0ZuyY_Ks1DKOH1-0juTQRgt9E1phewVWY761hw6oiUASXvTFBuCukmvu3-rhHGG2-CfTjgF5D52LU5pLYYtZVCmkM4X3GU5TTCgY5kSyp9y3_pu0FFeaALgMPA/s993/Captura%20de%20pantalla%202023-03-19%2017.22.17.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="519" data-original-width="993" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJTlKrSWff1tOWJE3UCt3IacBuoZdUc6AKDyh-tRvC3T_eR8Q0SSdCFie_LRBRb4BI0ZuyY_Ks1DKOH1-0juTQRgt9E1phewVWY761hw6oiUASXvTFBuCukmvu3-rhHGG2-CfTjgF5D52LU5pLYYtZVCmkM4X3GU5TTCgY5kSyp9y3_pu0FFeaALgMPA/w400-h209/Captura%20de%20pantalla%202023-03-19%2017.22.17.png" width="400" /></a></div><br /></div><h2 style="text-align: left;">Shift Left "<<"</h2><div>El operador "<<" mueve a la izquierda una secuencia de bits tantas veces como el número del lado derecho del operador lo indique.</div><div><br /></div><div>Para que quede un poco mas claro, tomemos como ejemplo el número 76 decimal, cuya representación en binario es 1001100 y lo queremos desplazar a la izquierda dos lugares, entonces tenemos:</div><div><ul><li>76 << 2 = 304</li></ul>Pero ¿por que? Para tener una visión mas clara de que es lo que sucede, veamos la representación binaria de la misma operación</div><div><ul style="text-align: left;"><li>1001100 << 10 = 100110000</li></ul>Si observas de manera detenida, veras que la secuencia "1001100" se movió a la izquierda, pero como no había mas números, simplemente se agregaron dos ceros para completar. En Python podemos crear el código siguiente para probar que estamos en lo correcto.</div><div><br /><pre>MiNum = 76
print (MiNum)
print("{:b}".format(MiNum))
MiNum <<= 0b10
print(MiNum)
print("{:b}".format(MiNum))</pre></div><div><br /></div><div><span> <div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwbdWISg0AHOYy__RCV4Zz4GiiSSCdt0QmgUkGEuLLfDnunJGZ5CLWOn5tXUUZni_DzQG3HdswCa9LvJJxyyo7OnzMnjWvCzMfKCPY3jhVcE1jZ3RXS1P0ODjCjW3WSNN6Dj6XbU6OgTAX9oYKhkfuCA02sRyqoM7tORYYT5svjfFx1FFaRZdGLc-l1A/s993/Captura%20de%20pantalla%202023-03-19%2017.47.09.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="519" data-original-width="993" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwbdWISg0AHOYy__RCV4Zz4GiiSSCdt0QmgUkGEuLLfDnunJGZ5CLWOn5tXUUZni_DzQG3HdswCa9LvJJxyyo7OnzMnjWvCzMfKCPY3jhVcE1jZ3RXS1P0ODjCjW3WSNN6Dj6XbU6OgTAX9oYKhkfuCA02sRyqoM7tORYYT5svjfFx1FFaRZdGLc-l1A/w400-h209/Captura%20de%20pantalla%202023-03-19%2017.47.09.png" width="400" /></a></div><br /></span><h2>Shift Right ">>"</h2><div>El operador ">>" mueve a la derecha una secuencia de bits tantas veces como el número del lado derecho del operador lo indique.</div></div><div><br /></div><div>Retomado número 76 pero ahora supongamos que los queremos desplazar cuatro lugares a la derecha, entonces tenemos:</div><div><ul style="text-align: left;"><li>76 >> 4 = 4</li></ul>Nuevamente revisemos los bits para entender que es lo que sucede:</div><div><ul style="text-align: left;"><li>1001100 >> 100 = 100</li></ul>Si observamos bien vemos que en efecto, todos los bits se desplazaron a la derecha, pero cómo en este caso llegamos al limite derecho, todos los bits sobrantes se descartaron. En Python podemos crear el código siguiente para probar que estamos bien:</div><div><br /><pre>MiNum = 76
print (MiNum)
print("{:b}".format(MiNum))
MiNum >>= 0b100
print(MiNum)
print("{:b}".format(MiNum))</pre></div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2lw0Ws48j69d6N1TxNr5GeJDeAZYMbaQ6EDoU5Xxn8JeCxbv2gxtsuhOjkHXBa-3oOgfTgekKtA_biVHjEPUXWlLM3y4j9xxh1JR9EwSxENrecivog0UCpn6k03LaurPoBjAKGxDQ91lMpAm3LgwzE3MxWiKSOlZjLPr5TEPdzLccpYqi17RWZBRkOw/s993/Captura%20de%20pantalla%202023-03-19%2018.03.36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="519" data-original-width="993" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2lw0Ws48j69d6N1TxNr5GeJDeAZYMbaQ6EDoU5Xxn8JeCxbv2gxtsuhOjkHXBa-3oOgfTgekKtA_biVHjEPUXWlLM3y4j9xxh1JR9EwSxENrecivog0UCpn6k03LaurPoBjAKGxDQ91lMpAm3LgwzE3MxWiKSOlZjLPr5TEPdzLccpYqi17RWZBRkOw/w400-h209/Captura%20de%20pantalla%202023-03-19%2018.03.36.png" width="400" /></a></div><br /><div style="text-align: left;"><br /></div><h2 style="text-align: left;">Not "~"</h2><div>El operador "~" simplemente invierte una secuencia de bits, hay que tomar en cuenta que numero estamos usando, y dependiendo del mismo, Python usará mas o menos bits para guardarlo en memoria, pero además hay que tener en cuenta que al aplicar el operador a números con signo (y no digo -1 o algo por el estilo, sino a los números enteros con signo donde un byte se utiliza para indicar si el número es negativo o no) este invertirá toda la secuencia por lo que si esperamos que; por ejemplo; 10 en binario lo invierta en 01 no será así.</div><div><br /></div><div>Para apreciar un poco más lo que sucede, tomemos el número entero de 8 bits 127, cuya representación en binario es 01111111 y es importante considerar el cero a la izquierda, ya que este nos dice que es un número positivo; al aplicar el operador ~ tenemos la secuencia de bits 10000000 que representa al número decimal -128.</div><div><br /></div><div>Y bien, por ahora es todo, en el siguiente post veremos algunos ejemplos de la vida real en donde saber usar la operaciones a nivel de bits es de mucha ayuda.</div><div><br /></div><div>Los leo luego</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-83605865399486253812023-02-11T16:14:00.000-06:002023-02-11T16:14:02.699-06:00Inútil apps #5 - Visualizer V3 (parte 3)Hola de nuevo a todos, después de tomar el sueño eterno (que en la actualidad es de seis a nueve meses!!) Por fin regresamos a terminar con los asunto pendientes y a tratar de aprender mas en el proceso. En el último post, estábamos fabricando unas luces que prenden y apagan al ritmo de la música.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPUES44lUp9vozCgq-PUy9I0r77DIfYIwQY-yfwMPD59mwSMt5KaSpNpOnPEOpAhVYvR6z8TDMs0x2OLBsH2ZSP0-NE9O4mhPcWHLM1pIDVkvU6B-2bIEj4Hkx1viJdG6uj-AoqWclRdEmfCJlEpks_hIZ1d2hkm7xxZli11toU9v6KFO4DfvJjp560w/s1169/Schematic_Visualizer%20V3_2023-02-11.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="828" data-original-width="1169" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPUES44lUp9vozCgq-PUy9I0r77DIfYIwQY-yfwMPD59mwSMt5KaSpNpOnPEOpAhVYvR6z8TDMs0x2OLBsH2ZSP0-NE9O4mhPcWHLM1pIDVkvU6B-2bIEj4Hkx1viJdG6uj-AoqWclRdEmfCJlEpks_hIZ1d2hkm7xxZli11toU9v6KFO4DfvJjp560w/s320/Schematic_Visualizer%20V3_2023-02-11.png" width="320" /></a></div><br /><div>Para poder comenzar vamos a ver el diagrama, esta disponible para su consulta en el sitio de <a href="https://oshwlab.com/mpm88g/visualizer-v3" target="_blank">OSHWLab</a>, el proyecto puede ser visto en línea o puedes descargarlo para poder abrirlo en Eagle o Altium y antes de empezar te recomiendo revisar las primeras dos partes de proyecto, <a href="https://xworkforall.blogspot.com/2022/05/inutil-apps-5.html" target="_blank">en la primera hablaamos sobre cómo amplificar la señal usando el circuito integrado LM358</a> y e<a href="https://xworkforall.blogspot.com/2022/05/inutil-apps-5.html" target="_blank">n la segunda parte, vimos cómo se hacen las conexiones para el LM3915</a>.</div><div><br /></div><div>Una vez dicho lo anterior, veamos todas las partes electricas que necesitaremos para el proyecto.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8r88KJXnd6bZ-MwfZ8qbhC3SuM5wiZ3DQXzvmtvjAdQAtpv3QpQP_4CI37QScc3cQqkVtHE4948_LBT5NzJya666TkuLzyYENxYUXRq_sqriDzzq6Gci9W6Aqq2JcTzp7TtVrrG6gjW20eOU6-qCiM7Y0W92g2G5CONLNYsiAuiB6GsSEUZe7I-Yptg/s4000/IMG_20230211_143216.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8r88KJXnd6bZ-MwfZ8qbhC3SuM5wiZ3DQXzvmtvjAdQAtpv3QpQP_4CI37QScc3cQqkVtHE4948_LBT5NzJya666TkuLzyYENxYUXRq_sqriDzzq6Gci9W6Aqq2JcTzp7TtVrrG6gjW20eOU6-qCiM7Y0W92g2G5CONLNYsiAuiB6GsSEUZe7I-Yptg/s320/IMG_20230211_143216.jpg" width="320" /></a></div><br /><div>Una cosa mas antes de continuar con la lista de partes, todos los pines que tengan el prefijo "H" (Por ejemplo H1, H2, etc) Y también los llamados "AUDIOINR" y "AUDIOINL" los omitiré de la lista, ya que en lugar de soldar pines, ahí soldaré directamente los cable y dependiendo de cada quien, se pueden poner los pines o no.</div><div><br /></div><div>ahora si, veamos la lista de componentes:<br /><table><thead><tr><th>MARCA</th><th>Valor</th></tr></thead><tbody><tr><td>R1,R4,R9,R10</td><td>10k</td></tr><tr><td>R2,R5</td><td>1k</td></tr><tr><td>R3,R6</td><td>200k</td></tr><tr><td>R7,R8</td><td>680</td></tr><tr><td>C1,C2</td><td>10uF</td></tr><tr><td>D1,D2</td><td>1N4007</td></tr><tr><td>U1</td><td>LM358</td></tr><tr><td>U2,U3</td><td>LM3915</td></tr><tr><td>LED1,LED2,LED3,...,LED20 </td><td> Diodo LED 5mm</td></tr><tr><td>POT1</td><td>Potenciómetro 100k</td></tr><tr><td>BUCK1</td><td>Buck converter</td></tr></tbody></table><br /></div><div>Nuevamente sugiero que revises la <a href="https://xworkforall.blogspot.com/2022/05/inutil-apps-5.html">parte 1</a> y la <a href="https://xworkforall.blogspot.com/2022/05/inutil-app-5.html">parte 2</a> de este proyecto para poder entender el "porqué" de cada valor. Los circuitos los pedí por JLCPCB y llegaron en tiempo y en forma (nadie me paga por decir lo anterior), recomiendo ampliamente el ordenarlo, pero si no confías en este tipo de servicios, también es posible hacerlo a mano en placas de prototipo.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyJ3_4RcWWPSKjv5J85hKImwu2fY9jK507__tHQnNdHNqHKw9yPykyh-KXG127BkWyxsuLBgXY7sEaCo1e1aOiuC3LS4YJrwDPa57LulRH1w-dPHsb9IzTQ7JV0Seab6bDxqbF5sjsSzD6h_8Fn7bzyWEpXed3h5Cw2VDV8wBazX4S9-l9fyUNKVXvDw/s4000/IMG_20230211_150107.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyJ3_4RcWWPSKjv5J85hKImwu2fY9jK507__tHQnNdHNqHKw9yPykyh-KXG127BkWyxsuLBgXY7sEaCo1e1aOiuC3LS4YJrwDPa57LulRH1w-dPHsb9IzTQ7JV0Seab6bDxqbF5sjsSzD6h_8Fn7bzyWEpXed3h5Cw2VDV8wBazX4S9-l9fyUNKVXvDw/s320/IMG_20230211_150107.jpg" width="320" /></a></div><br /><div>Una vez hechas las conexiones del diagrama, es necesario explicar para que sirven todos los pines.</div><div><ul style="text-align: left;"><li>AUDIOINR</li><ul><li>Sirve para conectar el audio, el canal derecho es recomendado, en el pin que tiene forma de cuadrado o PIN1 conectamos la parte positiva del audio, el pin 2 no se conecta y el pin 3 va al negativo del audio.</li></ul><li>AUDIOINL</li><ul><li>Igual que el anterior, sirve para conectar el audio de entrada, el canal izquierdo es el recomendado.</li></ul><li>H1</li><ul><li>Comparte las conexiones del pin 1 y tres de los pines "AUDIOINR", pero además, en este se encuentra la salida regulada del audio que se conecta a una entrada del LM358.</li></ul><li>H2</li><ul><li>Comparte las conexiones del pin 1 y tres de los pines "AUDIOINL", pero además, en este se encuentra la salida regulada del audio que se conecta a una entrada del LM358.</li></ul><li>H3</li><ul><li>Es la entrada de 5v</li></ul><li>H4</li><ul><li>Es la entrada de 12v</li></ul><li>U5 y U4</li><ul><li>Son los jumpers para seleccionar el modo punto o barra del LM3615</li></ul></ul>Para controlar el nivel del audio, usamos un potenciómetro de 100k, al inicio planee ponerlo en la placa pero debido a que mi visión fue cómo la del prototipo (o la foto de arriba). El potenciómetro, debe de ir en una de las paredes del gabinete, por lo que es necesario usar los cables. Para que la detección funcione, necesitamos hacer las conexiones cómo sigue:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ-qQ7y_aWl4XwPwTqjSn-enBBwhXHgcnZPmitgCiM9P4hzkoLGUrOteB9lq_T-xNrnT-cHomOjTWAKT_fqJXV3H1EOYG8KeNPCKThHnzxEAnXjKoj3bE_vG3KB8IUy6CSaUdXVIC5HRM_yKACRubKVAF1ibNjYb3bSeBH0duP4zpWRagR3aOcORwy7A/s1016/Captura%20de%20pantalla%202023-02-11%2015.15.49.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="764" data-original-width="1016" height="301" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ-qQ7y_aWl4XwPwTqjSn-enBBwhXHgcnZPmitgCiM9P4hzkoLGUrOteB9lq_T-xNrnT-cHomOjTWAKT_fqJXV3H1EOYG8KeNPCKThHnzxEAnXjKoj3bE_vG3KB8IUy6CSaUdXVIC5HRM_yKACRubKVAF1ibNjYb3bSeBH0duP4zpWRagR3aOcORwy7A/w400-h301/Captura%20de%20pantalla%202023-02-11%2015.15.49.png" width="400" /></a></div><br /><div>Y la razón por la que están compartidas las conexiones es simple, en mi caso, el las terminales del potenciómetro están conectadas a H1, pero queda libre "AUDIOINR" para poder conectar mi amplificador de audio y poder escuchar, algunos usan un plug de doble salida.</div><div><br /></div><div>Para alimentar el circuito, se pueden usar fuentes de 5 hasta 24v, y se conectan a H4, pero además es necesario usar una fuente adicional para obtener un voltaje inferior y conectarlo a H3. Seguramente te estás cuestionando ¿para qué?. La respuesta es simple, supongamos que alimentamos a H4 con 5v, este voltaje se va a usar para alimentar el integrado LM358 que hace la amplificación, pero si recordamos, el LM358 no es capaz de sacar un voltaje igual al que se usa para alimentarlo, es decir si amplificamos 0.2v diez veces, tendremos una señal de 2v a la salida, ahora si por ejemplo, tomamos 0.49v y le aplicamos el mismo novel de ganancia, tendríamos a la salida del LM358 4.5v (no exactamente); la idea central de todo esto, es que el voltaje siempre será menor al que usemos de alimentación y a la hora de ingresarlo al LM3915 no seria suficiente para cubrir el rango de este que iría de 0v a 5v. Para solucionarlo, decidí usar un voltaje diferente e inferior para alimentar al LM3915, retomando los 5v de ejemplo, si usamos ese, a H3 lo podemos alimentar con 3.3v usando el buck converter (incluso usar el LM7803) y con eso aseguramos que el rango vaya de 0 3.3v, asegurando que la señal siempre podrá cubrirlo. La configuración que yo uso es 12v para H4 y 5v para H3</div><div><br /></div><div>Finalmente para U4 y U5, si se hace un puente para cada una, es decir si los pines de U4 los unimos y hacemos lo mismo con U5; activaran el modo barra, si los dejamos abiertos, el LM3915 trabajará en modo punto.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEROhhg8j4938U89mYJ8qvp9ewKTyUZjPCSOYPWSLS2u-REhH7dhKEeEHWtQCClX2EzfI5bkZ6S7UplUNWRgmxSQa6x4VKd6jnCK5z28v87Nrd_KgjM3VvUao5xqoTk1RQGM-jpQFXpKhrXE-Zb-4p--Hc9CuU6sikm_Ew3AAXhC-lDUfcORqFxl_2lQ/s4000/IMG_20230211_144019.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEROhhg8j4938U89mYJ8qvp9ewKTyUZjPCSOYPWSLS2u-REhH7dhKEeEHWtQCClX2EzfI5bkZ6S7UplUNWRgmxSQa6x4VKd6jnCK5z28v87Nrd_KgjM3VvUao5xqoTk1RQGM-jpQFXpKhrXE-Zb-4p--Hc9CuU6sikm_Ew3AAXhC-lDUfcORqFxl_2lQ/s320/IMG_20230211_144019.jpg" width="320" /></a></div><br /><div><br /></div><div><br /></div><div>Y bien, por ahora es todo, es cierto que pasaron momento difíciles (se que habrá algunos preocupados) en el blog pero planeo seguir con esto lo mas que se pueda. y por cierto, dejo un video del proyecto en acción.</div><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dwAv7PhVX8ltJqQZ9ioAVeQTglQNdHflNHCwcM4etHv7y50uApuS3A7gLWpMA2WW-HteEXLwhWop2yq66p8lw' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div><br /><div><br /></div><div>Cómo de costumbre, los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-17994327704650639242022-06-21T14:02:00.007-05:002022-09-29T13:11:56.698-05:00Cumplimos 6 años.<p>Un día cómo hoy pero del 2016, nació el blog de Xwork y cómo cada año hay que celebrar; pero no en esta ocasión. Hoy solo rendiremos tributo a los que se adelantaron en el camino y solo esperan a que nos reunamos con ellos en la nada.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqozYIYuyZchfL2WGmDYV2PpRfn93yeRQpoyIUE66xxrpacEwAym6LCDMsoxEp5actI8AROliuA-eGAiGVKO9LeaWEwE8wnDvw4N_nIfKSsE2M9DY-Fsnu3KEvmEqkQ4K1istcE1RKaG2Brg_jzyrafxz8EsjV0UxDiYASaJaMDkbQ-PQ2F810MkBD8A/s1920/BlogX6.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqozYIYuyZchfL2WGmDYV2PpRfn93yeRQpoyIUE66xxrpacEwAym6LCDMsoxEp5actI8AROliuA-eGAiGVKO9LeaWEwE8wnDvw4N_nIfKSsE2M9DY-Fsnu3KEvmEqkQ4K1istcE1RKaG2Brg_jzyrafxz8EsjV0UxDiYASaJaMDkbQ-PQ2F810MkBD8A/w640-h360/BlogX6.jpg" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p>Att Xwork. Nos seguimos leyendo.</p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-20467952218873871442022-05-29T15:26:00.005-05:002022-11-14T10:03:53.224-06:00Inutil apps #5 - Visualizer V3 (parte 2)<p> Hola de nuevo a todos, el día de hoy vamos a continuar con más de Visualizer V3. En el post anterior, vimos cómo amplificar la señal de audio para que el nivel de las luces no dependa totalmente del volumen del audio, el día de hoy vamos a ver cómo hacer las pantallas de LEDs usando el viejo y confiable LM3915.</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2bQXsStYCV1jQJhJCw2P64gm0IJ9IoGxJBML4VLK3mMCcsIyfgfFNg2JbjHpiTaFbQUzzKTnkTOCDcpG0Lc6GvOMI8xzhH1z39ZGwlM3aEFNooqA2HMEmdkcXNEB39eV1_eguijx9ER55dN3VJ9szV6UzyDPo8VKiBSG29u00-iZy-xrC-ox5nzJLNA/s4000/IMG_20220514_235413.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2bQXsStYCV1jQJhJCw2P64gm0IJ9IoGxJBML4VLK3mMCcsIyfgfFNg2JbjHpiTaFbQUzzKTnkTOCDcpG0Lc6GvOMI8xzhH1z39ZGwlM3aEFNooqA2HMEmdkcXNEB39eV1_eguijx9ER55dN3VJ9szV6UzyDPo8VKiBSG29u00-iZy-xrC-ox5nzJLNA/s320/IMG_20220514_235413.jpg" width="320" /></a></div><br /><p>Las conexiones a seguir son realmente simples, para ello primero veamos el diagrama siguiente:</p><p><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTTvGVzTz8N694AmMZ9rCwirZbrPv0duzcqj9Q288WTLMbK4Um_BK58pBjWH22HjMiGuNHcav9TnwJfUScq_71dRTxhNvP7noa6hauKqpShyGYZ62oTtwN0OgcbP29jgYQ1tB5HCbTwzhlr7GaEP0D32ui6eorJ_XD7HoiJNxwnQZdP0IY2CjOegdaog/s1169/Schematic_Audio-meter_2022-05-29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="827" data-original-width="1169" height="226" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTTvGVzTz8N694AmMZ9rCwirZbrPv0duzcqj9Q288WTLMbK4Um_BK58pBjWH22HjMiGuNHcav9TnwJfUScq_71dRTxhNvP7noa6hauKqpShyGYZ62oTtwN0OgcbP29jgYQ1tB5HCbTwzhlr7GaEP0D32ui6eorJ_XD7HoiJNxwnQZdP0IY2CjOegdaog/s320/Schematic_Audio-meter_2022-05-29.png" width="320" /></a></div><br /><p>Las conexiones son:</p><p></p><ul style="text-align: left;"><li>Pin 1 - Salida de LED.</li><li>Pin 2 - Tierra.</li><li>Pin 3 - VCC (3 ~ 35V).</li><li>Pin 4 - Divisor bajo</li><li>Pin 5 - Input</li><li>Pin 6 - Divisor alto.</li><li>Pin 7 - Referencia de salida.</li><li>Pin 8 - Ajuste de referencia.</li><li>Pin 9 - Selector de modo.</li><li>Pin 10 ~ Pin 18 - Salida de LED's</li></ul>Si recuerdas del post <a href="https://xworkforall.blogspot.com/2018/06/inutil-apps-4.html" target="_blank">inutil apps #4 - Visualizador de sonido</a> y comparas los diagramas, verás que son prácticamente iguales, excepto por una sola conexión, en este caso el pin 6, lo conectamos a 5v, probablemente te estés preguntando ¿para que? y la respuesta es simple: así tenemos un mejor control de cual es el marco de trabajo.<p></p><p>En el post anterior vimos una de la aplicaciones de los amplificadores operacionales, en ese caso lo usamos cómo amplificador, pero es posible usarlo cómo comparador y el LM3915 hace uso de ese comportamiento.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7N6jFeNJPDL7EJoWn4hhpHvvQ-R1bynSJXN_i76wwSYe9iAJz58q-UsycjZEk5GcjWy9gENcOAEKJ2PF9uJRlXKThcsmUncJ_LQWNWvGo70iqpo3Az8YHtir0HStEm-EOA5F50jhg0psvKegB1qo4cDwNWQk7GhrZpQzqAZuTBnNklUZR7jP05R8GPw/s2646/lm3914.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2646" data-original-width="1557" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7N6jFeNJPDL7EJoWn4hhpHvvQ-R1bynSJXN_i76wwSYe9iAJz58q-UsycjZEk5GcjWy9gENcOAEKJ2PF9uJRlXKThcsmUncJ_LQWNWvGo70iqpo3Az8YHtir0HStEm-EOA5F50jhg0psvKegB1qo4cDwNWQk7GhrZpQzqAZuTBnNklUZR7jP05R8GPw/s320/lm3914.png" width="188" /></a></div><br /><p>Si observas detenidamente , todas las salidas son las salidas de los amplificadores operacionales, mientras que las entradas "positivas" se conectan a una serie de resistencias que van desde el pin 4 al pin 6, si recuerdas bien, <a href="https://xworkforall.blogspot.com/2019/10/back-to-basics-7.html" target="_blank">esta configuración es un divisor resistivo</a> y usarse resistencias del mismo valor en cada paso, tendremos que el voltaje será proporcional y será determinado por los valores que se usen en los pines 4 y 6. Así que si conectamos el pin 4 a tierra y el pin 6 a 5v, tendremos en cada paso 0.5v.</p><p>En la entrada negativa tendremos el voltaje que se aplique al pin 5, que en nuestro caso, será el voltaje proporcional al nivel de sonido. La forma en que se usa es: cuando a un amplificador operacional se le aplica un voltaje mayor en la entrada negativa, la salida de este tendrá un voltaje igual al aplicado a la entrada negativa, en caso contrario (si el voltaje es mayor en la entrada positiva), la salida tendrá el voltaje de la entrada positiva.</p><p>Si tomamos en cuenta quu aplicamos un voltaje de 5v en el pin 5 y el pin 6 esta conectado a 5v, eso implica que todos los led prenderán, pero además eso aplica también para cuando el voltaje es mayor a 5v (valga la redundancia). Ahora si por ejemplo; aplicamos 2.7v al pin 5 mientras los pin 4 y 6 siguen conectados igual, del primero al quinto LED son los que cumplen la condición de tener un voltaje mayor en la entrada negativa por lo que se encenderían.</p><p>Y bien, por ahora es todo, en el siguiente y ultimo post, agregamos los componentes adicionales y pondré a disposición de cualquiera los diagramas para la fabricación de los PCB, que recalco, ya han sido probadas y están listas para usarse.</p><p>Los leo luego.</p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-42714017849225695472022-05-15T17:45:00.001-05:002022-05-15T17:45:35.186-05:00Inutil apps #5 - Visualizer V3 (parte 1).<p> Hola de nuevo a todos, despues de un tiempo de ausencia, es hora de retormar el curso y crear un poco de contenido para el blog. En esta ocasion vamos a hacer mejoras para las luces audio-ritmicas que en algun momento hice. Si bien funcionan bien, había algunos detalles por arreglar para que funcionanran de manera adecuada. El proyecto se va a dividir en dos partes en donde detallaré cómo es que funciona, pero ademas pondré a disposicion de cualquiera que quiera ordenar las placas PCB, los archivos gerber del diagrama que veremos. Este a diferencia del velocimetro, ya esta probado, por lo que puedes encargarlos sin el temor de que algo funcione mal.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUNsW0nqud6l_fCA9dVVxi5uleJFUcJ7MljGUh4jZzdRk5jTr-8yMPo8ze_LOUuvN-TD47hgmYcjYz0GAxvvhEXZmtnR5CZN92HopLGiHriEinmaJN_UEsFbnJYyTAsRf-nwpgwdC1-zKJs3o1hjCq5S9wi5cZ-aPKkeDMYiNYVSxVLWZPir8NpevzDw/s4000/IMG_20220514_235413.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUNsW0nqud6l_fCA9dVVxi5uleJFUcJ7MljGUh4jZzdRk5jTr-8yMPo8ze_LOUuvN-TD47hgmYcjYz0GAxvvhEXZmtnR5CZN92HopLGiHriEinmaJN_UEsFbnJYyTAsRf-nwpgwdC1-zKJs3o1hjCq5S9wi5cZ-aPKkeDMYiNYVSxVLWZPir8NpevzDw/s320/IMG_20220514_235413.jpg" width="320" /></a></div><br /><p>Si hacemos un poco de memoria (<a href="https://xworkforall.blogspot.com/2018/06/inutil-apps-4-2.html" target="_blank">o revisamos el post</a>), veremos que todo funciona cómo es debido, pero tiene un problema un tanto grave: ¡¡No tiene control de volumen!!, por lo que los niveles que se muestran en las luces, dependen directamente del nivel de volumen de la fuente de sonido, pero no solo eso, además el sonido tenía que estar amplificado para poder ver algo. Al inicio lo pensé así por la sencilla razón que no tengo un amplificador grande; y eso fue hasta hace poco.</p><p>Ahora cómo el sistema de sonido es mas grande, al subir un poco el volumen , este ya alcanza un nivel considerable de ruido y aun así no se alcanza a iluminar todas las luces del diseño pasado. La solución es simple, debemos de amplificar por separado el audio para las luces. Para poder conseguir esa tarea, vamos a usar un amplificador operacional.</p><p>Para esta tarea cualquier amplificador operacional funciona, pero en mi caso voy a usar el circuito integrado <a href="https://www.ti.com/product/LM358" target="_blank">LM358</a>.</p><p>Para poder usarlo, debemos de hacer las conexiones cómo sigue:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjncNmVcj6-czizOECZI8TYaZsyS7vf4Ib0ZNtu8SU6SeLHBcLJ40nWUy8L7yc8iiQ9ccxk2DP_iq5shtDs75ZyfcVpKsNROgaq5v8oHw-hvvBYchV3zJVjKZ6hkET7svsa97yJspYp8qIDzQB-_BAkhlCd1_faw2iiCnQv-4u4lQ78m-ZZEZv0aTBjtw/s869/Captura%20de%20pantalla%202022-05-15%2016.00.46.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="654" data-original-width="869" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjncNmVcj6-czizOECZI8TYaZsyS7vf4Ib0ZNtu8SU6SeLHBcLJ40nWUy8L7yc8iiQ9ccxk2DP_iq5shtDs75ZyfcVpKsNROgaq5v8oHw-hvvBYchV3zJVjKZ6hkET7svsa97yJspYp8qIDzQB-_BAkhlCd1_faw2iiCnQv-4u4lQ78m-ZZEZv0aTBjtw/s320/Captura%20de%20pantalla%202022-05-15%2016.00.46.png" width="320" /></a></div><br /><p>Cómo podrás observar, usamos tres resistencias. R3 sirve para establecer a tierra cómo referencia entre la señal que ingresamos, R1 y R2 se utilizan para establecer la ganancia que va a tener. Hay que tener en cuenta que este tipo de conexión conocida cómo "<i>non inverting</i>" sirve para amplificar la ganancia de la señal (hay varios tipos de configuraciones, pero eso esta fuera del tema del post), pero además existe una formula que nos sirve para calcular la ganancia.</p><p>Tomando en cuenta que los voltajes de salida en los conectores de audio de la mayoría de los dispositivos esta en un rango de hasta 2.2v, debemos de tratar de elevarlo al menos diez veces. En la practica he tenido problemas asumiendo esto, debido a que no poseo un osciloscopio, me resulta difícil obtener una medición exacta, pero podemos aquí usar una ganancia de 200, aunque parezca algo exagerado, debemos de tener en cuenta la amplificación no va a ir mas allá del voltaje de entrada que le demos al amplificador, por lo que si lo alimentamos con 12v, en el peor de los casos, tendremos 11.7v aproximadamente cómo máximo a la salida (eso dependiendo del amplificador, pero una vez mas, es un tema que merece un post a parte). Pero además 2.2v es el máximo por lo que tener al mínimo el volumen podemos esperar valores en el rango de los milivolts y si por ejemplo; tenemos 0.01v, al amplificarlo 200 veces, tendremos 2v</p><p>Ahora veamos los cálculos.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVNERDwMO6Og0djpl3ubJ0x3Hcyppwqshiy9noqV-3IteVrzbFtpEixisCNwrQ0ztbM-KkfIS0QArvFhTwYJU8WXopTTkwlyF2c1xlcKWnqicrJPpBx-6jHGMIOyXBUOvQcyylbgdcTIBo-yqMOfjV8ts5Slp53Cmiv42a400WVrh5alZEky_zPpd_Ug/s278/GainF.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="137" data-original-width="278" height="137" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVNERDwMO6Og0djpl3ubJ0x3Hcyppwqshiy9noqV-3IteVrzbFtpEixisCNwrQ0ztbM-KkfIS0QArvFhTwYJU8WXopTTkwlyF2c1xlcKWnqicrJPpBx-6jHGMIOyXBUOvQcyylbgdcTIBo-yqMOfjV8ts5Slp53Cmiv42a400WVrh5alZEky_zPpd_Ug/s1600/GainF.png" width="278" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div><br /></div>Si queremos saber cual es el valor de las resistencias que debemos de usar, bastará con realizar la sustitución de dos valores, si por ejemplo queremos una ganancia de 201, pero además tenemos una resistencia de 1k para <i>R1 </i>podemos sustituir en la formula:<div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM_jlPlY5IHadRTvOMXHnd-Cb_8CyT-eAc3WO1JXMTbPL8Vv138Wxev3KujEMkCBl1Bokv_9BphL4ut9-Shrrz_-7fPnisoZScyVxor-J8sX-uSCl6ISvAfcTX2J1ffWokdss7fJnE_o9pkyeWZMijRKpJB-QkSLnv6YbkIJEukpdZVPPkRqIiAfd3nw/s352/GainF1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="125" data-original-width="352" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiM_jlPlY5IHadRTvOMXHnd-Cb_8CyT-eAc3WO1JXMTbPL8Vv138Wxev3KujEMkCBl1Bokv_9BphL4ut9-Shrrz_-7fPnisoZScyVxor-J8sX-uSCl6ISvAfcTX2J1ffWokdss7fJnE_o9pkyeWZMijRKpJB-QkSLnv6YbkIJEukpdZVPPkRqIiAfd3nw/s320/GainF1.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div><br /><p>Y después simplemente resolvemos:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX8AFvSn4iwB_Vadj1Sq-ZC5k8nTcvsGHgQLWn3jc_Yh8e-V2cH5wAZSC_KA8Cpp0SFN1ELf2ru0P0oXAAHE1eDHzxEUtI3xDuK10kAGK8qlVWtJJ7O5viTbjtMmg1jxyS-m_PhMne54yCjMMQGmZLcFBgCNyfqYe-u5X7CGIETlvBKXFWIrv6TFxmhA/s491/GainF2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="491" data-original-width="362" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX8AFvSn4iwB_Vadj1Sq-ZC5k8nTcvsGHgQLWn3jc_Yh8e-V2cH5wAZSC_KA8Cpp0SFN1ELf2ru0P0oXAAHE1eDHzxEUtI3xDuK10kAGK8qlVWtJJ7O5viTbjtMmg1jxyS-m_PhMne54yCjMMQGmZLcFBgCNyfqYe-u5X7CGIETlvBKXFWIrv6TFxmhA/s320/GainF2.png" width="236" /></a></div><br /><p>Es recomendable que se usen valores en el rango de los kilo ohms para <i>R1 </i>de esta forma evitamos algunos problemas, pero además, también es recomendable mantener la ganancia no tan alta, una ganancia de 300x es aceptable, pero hay que tener en cuenta que dependiendo del tipo de señal de entrada, del propio circuito integrado y otros factores, se puede introducir "ruido" o distorsión que normalmente no son deseables, pero para este tipo de proyecto, no afecta tanto.</p><p>Antes de continuar veamos un ejemplo donde tenemos el valor de R1 que es 100k; y R2 que es 50k; y solo queremos saber cual es la guanacia:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPa3BxRhUwVasC_Zs3W6J-VXVgKCRnp3g8jojY2eEYgnoBilc_CWcPNM2qX4ILmYI9bBBQsIaQakSAAkOU4FufG5DMOAsvqg2yAarGonykCLVRHurmExoN4N2rQ0mY9d0xiU9RXpGUWx0ylbiBanpRr_kAqXhHkCa-TUasxT7NDfSjOfPz0S9h_Zq0wg/s357/GainF3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="255" data-original-width="357" height="229" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPa3BxRhUwVasC_Zs3W6J-VXVgKCRnp3g8jojY2eEYgnoBilc_CWcPNM2qX4ILmYI9bBBQsIaQakSAAkOU4FufG5DMOAsvqg2yAarGonykCLVRHurmExoN4N2rQ0mY9d0xiU9RXpGUWx0ylbiBanpRr_kAqXhHkCa-TUasxT7NDfSjOfPz0S9h_Zq0wg/s320/GainF3.png" width="320" /></a></div><br /><p>Sabiendo lo anterior, podemos hacer las conexiones al lm358 tomando en cuenta cuales son los pines.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkWUU-C1NrKTQ_Aa_PSNBOn4-Y_XxJJZVOHkbLGOfJa8-EKUsvzGp9thYvYsFb63L3mKpuAF9SmAvRmcDpgczlQ7mozgIICKYZFb7ZmYuDyc5iy_LFZ9ACM1Gx_BqVkazkaHZQIUX7rET7M4l9F2EFbombJniULEBvYEncHX5__VhqBQEnozTpvwHjag/s803/Captura%20de%20pantalla%202022-05-15%2017.33.21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="424" data-original-width="803" height="169" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkWUU-C1NrKTQ_Aa_PSNBOn4-Y_XxJJZVOHkbLGOfJa8-EKUsvzGp9thYvYsFb63L3mKpuAF9SmAvRmcDpgczlQ7mozgIICKYZFb7ZmYuDyc5iy_LFZ9ACM1Gx_BqVkazkaHZQIUX7rET7M4l9F2EFbombJniULEBvYEncHX5__VhqBQEnozTpvwHjag/s320/Captura%20de%20pantalla%202022-05-15%2017.33.21.png" width="320" /></a></div><br /><p></p><ul style="text-align: left;"><li>El pin 4 se conecta a tierra.</li><li>El pin 8 se conecta a VCC, en nuestro caso 12v</li><li>El pin 3 es la entrada, aquí conectaremos nuestra señal de audio.</li><li>Entre el pin 1 y 2 conectaremos <i>R2 .</i></li><li>Entre el pin 2 y tierra, conectaremos <i>R1.</i></li><li>El pin 1 es la salida amplificada.</li></ul>Con las conexiones hechas, tendremos la señal lista para la siguiente parte (y no te preocupes no tardara un mes para salir).<p></p><p><br /></p><p>Y bien, por ahora es todo, si bien no hemos aprendido la capacidad real de los amplificadores operacionales, este post sirve cómo una aproximación a ellos. Recuerda que aquí no trato de enseñar los fundamentos de la electrónica, pero a lo mejor en el futuro, hago una explicación mas profunda de todo lo que se puede hacer. Recuerda que en el siguiente post publicaré la parte de las luces, pero además publicaré los esquemas para que puedas fabricar tu propia versión.</p><p>Los leo luego.</p></div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-43709639593416500522022-02-12T21:19:00.001-06:002023-10-28T16:55:40.132-06:00Vamos a programar #100 - Speedometer M. Versión Final.<p> Hola de nuevo a todos, el día de hoy empezaremos a ver la versión final del velocímetro.</p><p>Este proyecto fue iniciado mas o menos por el 2018 y ha sufrido una serie de cambios a lo largo del tiempo, si bien la última versión que fue publicada funcionaba bien, poco a poco se fueron agregando funciones para hacerlo funcionar de manera optima con el mínimo de hardware posible (de ahí que nunca le agregue ningún tipo de botón). Finalmente, tras días de pruebas y pruebas, he decidido finalizar con este proyecto porque finalmente ha alcanzado el nivel de confiabilidad para poder usarse en el día a día.</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiJE_QOeh_SryddilcfRbItcLUxr0E13D5KZVzsZtEQbODhiJngZuO5s4MTIIx0OcfUBUaFAjfldSg3omooVCMnFxDTGbWTeYN8JA6Z4PD3ehPyMBiaaDt_G3j3oefKeYX5xBo97GsMbJrL2mk-F0qYAOIXzHhK0d8fsh6V1fl7XoiOkwbAK0v90ufPmw=s4000" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEiJE_QOeh_SryddilcfRbItcLUxr0E13D5KZVzsZtEQbODhiJngZuO5s4MTIIx0OcfUBUaFAjfldSg3omooVCMnFxDTGbWTeYN8JA6Z4PD3ehPyMBiaaDt_G3j3oefKeYX5xBo97GsMbJrL2mk-F0qYAOIXzHhK0d8fsh6V1fl7XoiOkwbAK0v90ufPmw=s320" width="320" /></a></div><br />Primero que nada, veamos el código que hace funcionar a "Speeedometer M"<p></p><div><br /></div><div><pre>// Incluir la libreria para la pantalla
#include <LiquidCrystal.h>
// Pines usados en la pantalla extraido de los ejemplos incluidos en la libreria
const int rs = 13, en = 12, d4 = 11, d5 = 10, d6 = 9, d7 = 8;
// iniciar el objeto LiquidCrystal
LiquidCrystal lcd(rs , en , d4 , d5 , d6 , d7);
// Este es el valor que debes de modificar para que funcione correctamente
// Puedes ver instrucciones en https://xworkforall.blogspot.mx/2018/03/vamos-programar-49.html
const float Llanta = 1.9572;
//Algunas variables
const int HallSensor1 = 3;
const int LED1 = 7;
const int LED2 = 6;
const int LED3 = 5;
int Vuelta = 0;
unsigned long OldTime = 0;
float Distancia = 0.0;
float Speed = 0.0;
int ledState = LOW;
int VoltSensor = 0;
float BattVolt = 0.0;
// v=99.99
char BuffVelocity[15];
//d=999.99
char BuffDistance[15];
//R=999999
char BuffRevolution[15];
//V=4.2
char BuffBattery[15];
//Incializar todo
void setup() {
//Serial.begin(9600);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("@XWork");
pinMode(HallSensor1, INPUT);
attachInterrupt(digitalPinToInterrupt(HallSensor1) , GetVelocity , LOW);
pinMode(LED1 , OUTPUT);
pinMode(LED2 , OUTPUT);
pinMode(LED3 , OUTPUT);
}
// Calcular la velocidad
void GetVelocity() {
if (millis() - OldTime > 70) {
Speed = Llanta / ((float)( millis() - OldTime) / 1000) * 3.6;
OldTime = millis();
Distancia = Distancia + Llanta / 1000;
Vuelta += 1;
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
}
}
//Bucle principal
void loop() {
VoltSensor = analogRead(A5);
BattVolt = (VoltSensor * (10.0 / 1023.0));
dtostrf(Speed , 5 , 2 , BuffVelocity);
dtostrf(Distancia , 6 , 2, BuffDistance);
sprintf(BuffRevolution , "R=%6d", Vuelta);
dtostrf(BattVolt , 5 , 1, BuffBattery);
lcd.setCursor(0, 0);
lcd.print("d=");
lcd.setCursor(2 , 0);
lcd.print(BuffDistance);
lcd.setCursor(9, 0);
lcd.print("v=");
lcd.setCursor(11, 0);
lcd.print(BuffVelocity);
lcd.setCursor(0,1);
lcd.print(BuffRevolution);
lcd.setCursor(9,1);
lcd.print("V=");
lcd.print(BuffBattery);
digitalWrite(LED1 , ledState);
digitalWrite(LED2 , ledState);
digitalWrite(LED3 , ledState);
if ((millis() - OldTime) > 3000){
ledState = LOW;
Speed = 0.0;
}
}
</pre><div><br /></div><br /></div>Si haz seguido el proyecto desde el inicio, te darás cuenta que la mayoría de las funciones han cambiado un poco y algunos de los elementos que en ocasiones usamos para algunos post, los retomamos para llegar a está versión. Antes de continuar, voy a responder a unas cuantas preguntas que surgieron durante todo el proceso. La primera de ellas: ¿Cuánta es la máxima distancia que se puede alcanzar con el diseño original? La respuesta es relativamente sencilla, ya que usamos seis espacios de la pantalla y usamos tres para los enteros, una para el punto y dos para los decimales; la máxima distancia posible es 999.99 km, que es mas que suficiente para la mayoría de las personas, pero aquí es donde viene la parte "truculenta". La parte donde se llevan la cuenta de las revoluciones, el máximo posible es de 999,999 vueltas, en el caso de una llanta 24x2.25, la circunferencia es de 1.95m por lo que al multiplicar por el número de vueltas máximo obtenemos 1,949,998.05m máximo o lo que es lo mismo 1949km y esto bastaría para desbordar la parte de la distancia.<div><br /></div><div>Otro dato que cambio fue la información que se usa para la batería. En algún momento del desarrollo, pensé en incluir un icono de batería, pero cada una de las baterías son diferentes y por lo tanto no es factible escribir el código ya que es casi una garantía que la pila no es igual en la mayoría de los casos. y por ejemplo, la pila que yo uso era de un teléfono y traía consigo una protección que corta la corriente cuando la pila alcanza 2.5v, pero una pila Sony VTC6 recomienda que el voltaje nunca baje de los 3v.</div><div><br /></div><div>Es posible usar una pantalla de 20x4, pero la 16x2 es mas compacta y además, permite tener todo de manera lo mas sencilla posible.</div><div><br /></div><div>Por último, he actualizado el proyecto en <a href="https://oshwlab.com/mpm88g/Speedometer" target="_blank">easyeda </a>y he hecho el PCB de la mejor manera posible y es posible ordenarlos, pero quiero resaltar que aun no los he probado (o no al momento de que esto fue escrito) y eso se debe a que van a tardar un par de semanas. De todos modos, se pueden seguir las conexiones del diagrama y hacerlo en un placa perforada a mano.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhfomjR6ujxhK_RoZKqyFYrexdrXX55a6v_2Nj2BLpA_5-0yy1cAYnOHHGreEqecGiuwvZXaZP4ZeYJCBO-TiFgMpNS6Q-3flzDyi-MCBf1ZC9Zb4GetGCLWfOu_9YWm7ba_Kh0UysnYQXRNjEh00lu129y_LiO7Ctb2EsxBb-i5tGBNd_NjuW93_xmlw=s1169" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="827" data-original-width="1169" height="226" src="https://blogger.googleusercontent.com/img/a/AVvXsEhfomjR6ujxhK_RoZKqyFYrexdrXX55a6v_2Nj2BLpA_5-0yy1cAYnOHHGreEqecGiuwvZXaZP4ZeYJCBO-TiFgMpNS6Q-3flzDyi-MCBf1ZC9Zb4GetGCLWfOu_9YWm7ba_Kh0UysnYQXRNjEh00lu129y_LiO7Ctb2EsxBb-i5tGBNd_NjuW93_xmlw=s320" width="320" /></a></div><br /><div>O mandarlas a fabricar (que repito, aun no he probado.)</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg2NsRleLGtw3OnP28f89PtCFEV3Vaf8TFh240ZRObH-RDwtT57mgnLSfiMMNo9luL9icmTqaTQ20feKxEfQH5vXVpu3lhUABkoq4u9V8N90XBqObRgxDhAMyFtapaqhRFvUM3wTcvK0BUyuXIvEqSEVQK3upzVx7EozPWIiT3tbsGAEuOhFrAlLaUWMg=s1678" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="736" data-original-width="1678" height="140" src="https://blogger.googleusercontent.com/img/a/AVvXsEg2NsRleLGtw3OnP28f89PtCFEV3Vaf8TFh240ZRObH-RDwtT57mgnLSfiMMNo9luL9icmTqaTQ20feKxEfQH5vXVpu3lhUABkoq4u9V8N90XBqObRgxDhAMyFtapaqhRFvUM3wTcvK0BUyuXIvEqSEVQK3upzVx7EozPWIiT3tbsGAEuOhFrAlLaUWMg=s320" width="320" /></a></div><br /><div>Y bien por ahora es todo, tal y cómo lo mencioné al principio, ya no voy a publicar mas actualizaciones, pienso que todo funciona cómo debe, pero además todo está disponible para que cada quien lo modifique a cómo lo necesite, <a href="https://easyeda.com/mpm88g/Speedometer" target="_blank">recuerda que puedes hacer el pedido desde JLCPCB</a> y además <a href="https://www.dropbox.com/s/t3xen1bfkq6wemv/Speedometer.rar?dl=1" target="_blank">puedes descargar el código desde mi DropBox</a> para que lo modifiques a tu gusto.</div><div><br /></div><div>Los leo luego</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com2tag:blogger.com,1999:blog-4516612096157502273.post-84645938741675271992022-01-16T14:19:00.005-06:002023-08-19T10:22:57.722-06:00Vamos a programar #99 - Modo obscuro en CSS 3.<p> Hola de nuevo a todos, el día hoy vamos a ver como aplicar el modo obscuro a una página web de manera sencilla.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg95BC4TRXZ9tseIzN7zSbAE7q1Xtya4-iZoNl-flvqaV4ucY-eXV4T0vxQKFRVbzrCE-J2H0ypGYsAkhH5zJp53NRrCcKQiCjrORLd1rhbeldEJdM6Ye_xEjlZkBeObxUKDg7aNMuUjEyJgmeZeYYGcjzTylBuODsLfkn2LkrfL7ARu8gpUqxrfqgZAw=s864" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="529" data-original-width="864" height="392" src="https://blogger.googleusercontent.com/img/a/AVvXsEg95BC4TRXZ9tseIzN7zSbAE7q1Xtya4-iZoNl-flvqaV4ucY-eXV4T0vxQKFRVbzrCE-J2H0ypGYsAkhH5zJp53NRrCcKQiCjrORLd1rhbeldEJdM6Ye_xEjlZkBeObxUKDg7aNMuUjEyJgmeZeYYGcjzTylBuODsLfkn2LkrfL7ARu8gpUqxrfqgZAw=w640-h392" width="640" /></a></div><br /><p><br /></p><p>Si te has dado vueltas por el blog, podrás observar que ahora, dependiendo de la configuración del sistema operativo-navegador, la página cambia de color, si el tema es obscuro, la página también lo será y si es claro, pasa lo mismo.</p><p>Para poder aplicar un tema de un color para cada caso, existe una forma sencilla de hacerlo y para poder apreciarlo mejor, vamos a ver un ejemplo con los estilos para la calculadora de Vigenere.</p><p>Para empezar, veamos cuales son los estilos actuales (dependiendo de cuando leas esto, los estilos pudieron haber cambiado, para que no haya problema puedes ir y revisar el código fuente de la página en vivo).</p><div><pre>.h1v
{
font-size: 16px;
}
.formVigenere{
font: 95% Arial, Helvetica, sans-serif;
max-width: 400px;
margin: 10px auto;
padding: 16px;
background: #000;
}
.textareaV
{
width: 100%;
height: 150px;
padding: 5px 8px;
box-sizing: border-box;
border: 2px solid #999;
border-radius: 4px;
resize: none;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
}
.textareaV:focus
{
width: 100%;
height: 150px;
padding: 2px 5px;
box-sizing: border-box;
border: 2px solid #50b;
border-radius: 4px;
resize: none;
}
.TextV
{
width: 100%;
box-sizing: border-box;
border: 2px solid #999;
border-radius: 4px;
}
.ButtonV
{
width: 100%;
background-color: #57a2e2;
border: none;
border-radius: 4px;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
}
.ButtonV:hover
{
width: 100%;
background-color: #224360;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
}</pre></div>En CSS3, podemos definir clases que serán estilos para los objetos, cada vez que se le asigne una clase a un objeto, si está definido, este tomara la apariencia que la clase diga, pero además es posible sobrecargar ciertos estilos. En este caso, el primero paso para poder aplicar un tema obscuro, debemos de definir los estilos que serán predeterminados y para esto asumiremos que el estado normal siempre será el tema claro y la mayor parte del tema se basara en este.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj5wXvV7mVXGT_nB6VzBgCsj4ElXCr0h4ouEpA9iNoOlFpA5e9RxPESXtjqEGgLBpl7x1YWGVcLIRVRayk_87BK6G_7iY4XtktKQhWyOtFntrd1mc8hI6seCfUGMjcwc3K2EditZT24G0vdx9-kALp3CgAZI_6ScG1v1B-Lv79jFOXblDAVFLr4-vGe3Q=s529" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="529" data-original-width="432" height="320" src="https://blogger.googleusercontent.com/img/a/AVvXsEj5wXvV7mVXGT_nB6VzBgCsj4ElXCr0h4ouEpA9iNoOlFpA5e9RxPESXtjqEGgLBpl7x1YWGVcLIRVRayk_87BK6G_7iY4XtktKQhWyOtFntrd1mc8hI6seCfUGMjcwc3K2EditZT24G0vdx9-kALp3CgAZI_6ScG1v1B-Lv79jFOXblDAVFLr4-vGe3Q=s320" width="261" /></a></div><br /><div><br /><div>Si observamos el caso de nuestro formulario tenemos que el texto es de color negro, el fondo es de color gris claro, los botones son de color azul claro y el área de las cajas de texto es blanco. Para poder idear un esquema de colores para el caso del tema obscuro, uno pensaría que bastaría con invertir los colores, pero no siempre es tan sencillo.</div></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgK7yUn2PMM5pf9Z51Ycio_T2YG6btjgNYJddhstAVS2sXQFDGCKlPyKdyguQ__W3RgvbRBIk3FnFxYCIT2LGciV5WnKF_JTRcYJTaahgSXS5mQFQK0dn5WGz071NbN7NAZ9RcAkiinhlFYHOTSd7FYOlQxLvD6GMcFFrkHjp9mrH4NSfIkvg9KIpvprA=s529" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="529" data-original-width="432" height="320" src="https://blogger.googleusercontent.com/img/a/AVvXsEgK7yUn2PMM5pf9Z51Ycio_T2YG6btjgNYJddhstAVS2sXQFDGCKlPyKdyguQ__W3RgvbRBIk3FnFxYCIT2LGciV5WnKF_JTRcYJTaahgSXS5mQFQK0dn5WGz071NbN7NAZ9RcAkiinhlFYHOTSd7FYOlQxLvD6GMcFFrkHjp9mrH4NSfIkvg9KIpvprA=s320" width="261" /></a></div><br /><div>Si observamos la imagen de nueva cuenta, es el resultado de invertir los colores y si no está tan mal, hay que tener en cuanta cual es el color base del tema, en el caso del blog, la mayoría de elementos son de color azul y deberíamos de tratar de preservar la mayor parte de elementos de este color. </div><div><br /></div><div>Analizando las partes del formulario, observamos que consta de los siguientes elementos: etiquetas, cajas de texto, botones de opción, botones y el área del formulario. Si por ejemplo observamos que las etiquetas tienen el fondo transparente y el color de las letras es negro, bastará con cambiar el área del formulario que directamente afecta el color de fondo de las etiquetas, que al ser transparentes tomaran el color del área del formulario y por lo cual solo debemos de ocuparnos del color del texto. Para eso bastara con tener en cuenta que para el área del formulario queremos un color muy oscuro o simplemente negro, para las etiquetas vamos a querer un color claro o blanco y para poder elegir uno adecuado, podemos usar las herramientas que nos ofrecen la mayoría de navegadores.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgmsLKt81kk9n09OcJG7ekzTWvGZ2kHdmrzyccxCnH51dah4jUTngjMuBp9Rai-qqTHaQC0Ls3EnFjVJdmDZsPmNFLHsmGgPwyuTBmGVK1u4sZMBX4BLr6ogRpJ65MCn8RNVe-LVacZjxAxmud-1q69wIwoJ2KOuEDggtSKYL3NKBGkcMq7WsD8OdsibA=s632" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="344" data-original-width="632" height="174" src="https://blogger.googleusercontent.com/img/a/AVvXsEgmsLKt81kk9n09OcJG7ekzTWvGZ2kHdmrzyccxCnH51dah4jUTngjMuBp9Rai-qqTHaQC0Ls3EnFjVJdmDZsPmNFLHsmGgPwyuTBmGVK1u4sZMBX4BLr6ogRpJ65MCn8RNVe-LVacZjxAxmud-1q69wIwoJ2KOuEDggtSKYL3NKBGkcMq7WsD8OdsibA=s320" width="320" /></a></div><br /><div>En google chrome si presionamos F12 se abrirá las herramientas para desarrolladores y podremos inspeccionar los elementos que la conforman, al poner el ratón sobre un elemento, se mostraran algunas propiedades y veremos que hay una que dice "<i>Contrast</i>" si aparece un icono de exclamación (cómo en la imagen anterior) quiere decir que la combinación de colores podría resultar difícil de leer.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi0Lex8z3qjmFy4qq2GbKayUZWaZrD5Yw9oTkxE-TGmOPphI7agke_3GwT5dNfL4nWWdbrm3Jy9lmuoPcmvZ2oi76FvmUuzEQJzy6XatHPbPY4NMqslub0GO1fj-Wj8dbLuTcKyxvAkwi8boGArncR4AXckZcr0r3ViVDHeUPWyDEo3yxTN6ALSTXRdFQ=s692" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="380" data-original-width="692" height="176" src="https://blogger.googleusercontent.com/img/a/AVvXsEi0Lex8z3qjmFy4qq2GbKayUZWaZrD5Yw9oTkxE-TGmOPphI7agke_3GwT5dNfL4nWWdbrm3Jy9lmuoPcmvZ2oi76FvmUuzEQJzy6XatHPbPY4NMqslub0GO1fj-Wj8dbLuTcKyxvAkwi8boGArncR4AXckZcr0r3ViVDHeUPWyDEo3yxTN6ALSTXRdFQ=s320" width="320" /></a></div>En cambio si aparece una marca verde, eso indica que la combinación es adecuada para la lectura, pero además nos sirve para crear nuestro esquema de colores.<div><br /></div><div>La ventaja del color azul que seleccioné para el blog es que resalta, pero no solo en colores oscuros, si no también en los obscuros, por eso podemos cambiar el área del formulario, el color de las letras en las etiquetas y con eso bastaría para que la lectura sea agradable em ambos modos: obscuro y claro.</div><div><br /></div><div>Una vez que hemos determinado que es lo que se va a cambiar, en CSS3 existe una clase o consulta (queries) en la cual se va a aplicar cierto estilo basado en la configuración del sistema operativo, en este caso va a ser en el aspecto, tanto windows cómo Mac os tienen un ajuste en el cual se puede establecer si la mayor parte de los elementos serán claros o obscuro, pero además la mayoría de los navegadores tienen está misma opción.</div><div><br /></div><div>Para hacer uso debemos usar "<i>@media (prefers-color-scheme: dark)</i>" y cuando el sistema operativo, navegador diga que se debe de usar el tema oscuro, todas las clases que estén aquí se aplicaran automáticamente sobrecargando las actuales (si se usan).</div><div><br /></div><div>en nuestro caso, veamos que la definición para el área del formulario es:</div><div><pre>.formVigenere{
font: 95% Arial, Helvetica, sans-serif;
max-width: 400px;
margin: 10px auto;
padding: 16px;
background: #eee;
}</pre></div><div><br /></div><div>para hacer el cambio, dentro de "<i>@media (prefers-color-scheme: dark)"</i>, debemos de crear una subclase que sera igual a la queremos modificar, en este caso, ".<i>formVigenere</i>" y solo incluiremos las propiedades que se van a cambiar en función al color, por lo que tendríamos algo así:</div><div><pre>@media (prefers-color-scheme: dark) {
.formVigenere{
background: #000;
}</pre></div><div>Para cada elemento iremos haciendo lo mismo hasta cubrir los cambio necesarios.</div><div><br /></div><div>Y bien, por ahora es todo, cómo podrás ver, resulta sencillo hacer el cambio de esquemas y sirve de introducción para poder crear una variedad de temas y no solo claro y obscuro.</div><div><br /></div><div>Los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-42970331853821412282021-12-30T21:44:00.000-06:002021-12-30T21:44:05.214-06:00BIenvenido el 2022.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg_gbrZiHtHpZZBmJ8q-eAxvjOGLU88-V9YT2sU_6utSr_w5EMkBRZmqti7YPIsGngQMe7vYjDbcz3Z63Xd3zaTX7orJW4YQ-9R8eh2bd4NlSCC083KjEcVw_7F8VGk9bC742TQtT6WmgXbV7wtS5bgmThuOizK0vumFbiRJJ7tXCt7smLrktVRKGXyWQ=s1920" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="180" src="https://blogger.googleusercontent.com/img/a/AVvXsEg_gbrZiHtHpZZBmJ8q-eAxvjOGLU88-V9YT2sU_6utSr_w5EMkBRZmqti7YPIsGngQMe7vYjDbcz3Z63Xd3zaTX7orJW4YQ-9R8eh2bd4NlSCC083KjEcVw_7F8VGk9bC742TQtT6WmgXbV7wtS5bgmThuOizK0vumFbiRJJ7tXCt7smLrktVRKGXyWQ=s320" width="320" /></a></div><br /><p><br /></p><p>Hola de nuevo a todos, el día de hoy, solo escribo para desearles a todos un próspero año 2022. El año 2021 fue realmente difícil y mucha de la gente valiosa simplemente se nos adelanto en el camino. Hay que seguirnos cuidando y seguir trabajando para que el año venidero sea mas llevadero.</p><p>El blog ha sufrido una caída importante y no he podido publicar cómo es debido, pero le hice la promesa a algunas personas de que este proyecto bajo ninguna manera lo voy a dejar morir, así que simplemente tratare de mantener la promesa de un buen post al mes.</p><p>Sin mas que decir, espero que sigas pasándote por el blog de XWork y que algo de lo que aquí hay sea de utilidad.</p><p>Nos seguimos leyendo.</p><p>"In the 8th day god said: 'Let there be light' and there was Sir Isaac Newton."</p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-56164673754436439652021-11-08T17:18:00.004-06:002021-11-08T17:19:28.624-06:00Vamos a programar #98½ - Ver el rendimiento del PC usando arduino y C#.<p> Hola de nuevo a todos, el día de hoy vamos a ver cómo es posible crear una barra de progreso en una pantalla LCD.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXoo-C8CYwl0SCGY55K5nyzHEv2vNj0Twcb-VyZahkRu5NF6DmoGrAKNb1PDgG7D8LUIFDgwMJ_j5nf8ifPBEy5D18lNCD9ZFy5Ro7zbEhxALGgjmnW2xd2B2uOfLk7CSfmOTvzQBcUQmh/s4000/IMG_20211106_155323.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXoo-C8CYwl0SCGY55K5nyzHEv2vNj0Twcb-VyZahkRu5NF6DmoGrAKNb1PDgG7D8LUIFDgwMJ_j5nf8ifPBEy5D18lNCD9ZFy5Ro7zbEhxALGgjmnW2xd2B2uOfLk7CSfmOTvzQBcUQmh/s320/IMG_20211106_155323.jpg" width="320" /></a></div><br /><p><br /></p><p>En el post anterior vimos cómo es posible usar arduino para monitorear el uso del CPU y la memoria RAM. Usando el programa, era posible visualizar el porcentaje en dos barras de progreso. Si bien todo el programa funcionaba, no lo hacia del todo bien, ya que el procedimiento que se encargaba de hacerlo tenia un par de cosas que le hacían falta y que no consideré hasta que escribí el poste anterior.</p><p>Algunas personas me mandaron sus soluciones, y de hecho de eso se trata el blog, no solo de enseñar, si no de aprender todos juntos. Por eso en el post del día de hoy, veremos paso a paso cómo corregir esa situación.</p><p>Para empezar vamos a entender cómo es que la barra funciona, en el post anterior, vimos cómo es que se crea cada parte, ahora veamos la lógica. Si hacemos memoria, el código de la barra es el que sigue</p><div><pre>void DrawProgressBar(uint8_t Value, uint8_t XLocation, uint8_t YLocation){
uint8_t Progress = map(Value , 1 , 100 , 0 , 20);
lcd.setCursor(XLocation , YLocation);
for (uint8_t i = 0; i < 20; i++)
{
if (i == 0)
{
if (Progress == 0)
lcd.write(0);
else
lcd.write(1);
}
else if (i == 19)
{
if (Progress == 20)
lcd.write(3);
else
lcd.write(2);
}
else
{
if (Progress <= i)
lcd.write(4);
else
lcd.write(5);
}
}
}</pre><br /></div>Para ver cual es el error, bastará con asignarle algunos valores diferentes de los que trae. La función recibe tres parámetros, el primero es el valor que se quiere representar y debe de ser un valor entre 0 y 100 (o un porcentaje), el segundo es el valor en el cual se posicionara en el eje X y finalmente, el tercero sirve para indica cual será su ubicación en el eje Y. Pero que pasa si usamos otros valores cómo 0 y 3 (solo para las ubicaciones) o 10 y 3. Bueno tendremos algo cómo lo que sigue:<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ9oGPpbKG7esYOcNjiZG74Y5DC6mRJcXHHyg8w35DbHR13u0FoVYL8y0eycXqmKfEa4zUuJCmyb9uzBOVQQ8rhWbCvmr8KmBTH4JHE-F7rgyuqBDGgzkGHaclZ2bZ7GZkR5AAA0WjFrQl/s4000/IMG_20211106_162510.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ9oGPpbKG7esYOcNjiZG74Y5DC6mRJcXHHyg8w35DbHR13u0FoVYL8y0eycXqmKfEa4zUuJCmyb9uzBOVQQ8rhWbCvmr8KmBTH4JHE-F7rgyuqBDGgzkGHaclZ2bZ7GZkR5AAA0WjFrQl/s320/IMG_20211106_162510.jpg" width="320" /></a></div><br /><div>La razón por la que ocurre es que no podemos simplemente poner código y esperar a que funcione, en algunos de los post previos, vimos a grandes rasgos cómo es que funciona la pantalla y el resultado anterior es debido a que primera línea, continua en la tercera y la segunda continua en la cuarta, entonces si escribimos la barra con su tamaño "completo" al llegar al linte se la primera o segunda línea, esta continuara en la tercera o cuarta.</div><div><br /></div><div>Para poder resolver el problema, vamos a tomar en cuenta cuales son las cosas que tenemos. Para empezar, hay que considerar el tamaño de la pantalla, cómo es algo que no cambia (para cada caso individual), una vez que tenemos identificado el tamaño, lo podemos asignar a unas constantes, para eso, justo antes de crear el objeto de pantalla "<i>lcd</i>" definimos a "<i>SCREEN_SIZE_X</i>" que será el tamaño de la pantalla en el eje X y que en mi caso es 20, también definimos "<i>SCREEN_SIZE_Y</i>" que será el tamaño de la pantalla y que en mi caso es 4. Para el caso de una pantalla de 16x2, bastara con asignar a "<i>SCREEN_SIZE_X</i>" con 16 y a "<i>SCREEN_SIZE_Y</i>" con 2. Cuando creamos el objeto "<i>LiquidCrystal_I2C</i>", pasamos el primer parámetro "0x27" que es la dirección por la cual se comunicará (y puede cambiar pero por ahora cómo es el único dispositivo I2C conectado), luego se pasa el parámetro del tamaño en X de la pantalla, seguido por el del eje Y.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhNsbZBQa9iOVhyphenhyphenEe7uMtNk9UxxEXpbMvGFsEcMJMemKOQYC1-pfb_TlsO0H_moOxp-7Lc5mbrTvY9RrR78rE-UpllgYMvSofrL4bTBxWSm6kNLgUwNDEqTUoOraOMRAlXBft6ftpzAqMZ/s1194/bar1.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="1194" height="151" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhNsbZBQa9iOVhyphenhyphenEe7uMtNk9UxxEXpbMvGFsEcMJMemKOQYC1-pfb_TlsO0H_moOxp-7Lc5mbrTvY9RrR78rE-UpllgYMvSofrL4bTBxWSm6kNLgUwNDEqTUoOraOMRAlXBft6ftpzAqMZ/w400-h151/bar1.jpg" width="400" /></a></div><br /><div><br /></div><div><br /></div><div>Ahora que tenemos identificado el tamaño de la pantalla, podemos hacer los cálculos de manera mas sencilla en el procedimiento "<i>DrawProgressBar</i>", la barra esta pensada para que se dibuje desde la ubicación del parámetro "<i>XLocation</i>" hasta el final de la pantalla, entonces si ahora que tenemos el tamaño de la pantalla, para determinar el tamaño de la barra, bastará con restar el tamaño de la pantalla menos la ubicación en el eje X, el resultado de está operación lo podemos asignar a una variable y así en lugar de iterar de uno a un número fijo, lo haremos solo hasta lo que debe de ser la longitud de la barra. Así podemos hacer un código cómo el que sigue solo para la barra y las definiciones del tamaño.</div><div><div><pre>//Para las pantallas de 16 x 2, basta con reemplazar el 20 por 16 y el 4 por 2
#define SCREEN_SIZE_X 20
#define SCREEN_SIZE_Y 4
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27 , SCREEN_SIZE_X , SCREEN_SIZE_Y);
//{..............}
//Barra de progreso
void DrawProgressBar(uint8_t Value, uint8_t XLocation, uint8_t YLocation){
uint8_t BarSize = SCREEN_SIZE_X - XLocation;
uint8_t Progress = map(Value , 1 , 100 , 0 , BarSize);
lcd.setCursor(XLocation , YLocation);
for (uint8_t i = 0; i < BarSize; i++)
{
if (i == 0)
{
if (Progress == 0)
lcd.write(0);
else
lcd.write(1);
}
else if (i == BarSize - 1)
{
if (Progress == BarSize)
lcd.write(3);
else
lcd.write(2);
}
else
{
if (Progress <= i)
lcd.write(4);
else
lcd.write(5);
}
}
}</pre><br /></div>Ahora si miramos la imagen anterior, para poder usar una barra similar, bastará con usar "100 , 11 , 1" cómo parámetros. Hay que resaltar que el tamaño de la barra debe de ser mínimo de tres por lo que la posición máxima en X es 17, cualquier número más allá de el, hará que la barra no se muestre correctamente.</div><div><br /></div><div>Y bien, por ahora es todo, <a href="https://www.dropbox.com/s/q8kkd24a52g4cg8/CPUMonitor.rar?dl=1" target="_blank">el vínculo actualizado como de costumbre lo puedes descargar de mi dropbox, para que lo modifiques a tu gusto </a>(si por ejemplo quieres cambiar el tamaño sin que pegue en la orilla).</div><div><br /></div><div>Los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-32199565628186635732021-10-24T13:27:00.003-05:002021-11-02T14:51:03.659-06:00Vamos a programar #98 - Ver el rendimiento del PC usando arduino y C#.<p> Hola de nuevo a todos, el día de hoy vamos a ver cómo ver el rendimiento del PC usando C# y arduino.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtuRyTXvvBJcxEbWbcV4r06FHEPv-AfaMI1PjrxO-3onimXwETLvNKMtknHw76vhmj-9rTElV92wPxzsiQ-qGCgqwdTg6ta5rBwpjD0gNpT0PNIFIEv641H5qtIOhQXTR83oWuv5nV7_Xx/s4000/IMG_20211017_232049.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtuRyTXvvBJcxEbWbcV4r06FHEPv-AfaMI1PjrxO-3onimXwETLvNKMtknHw76vhmj-9rTElV92wPxzsiQ-qGCgqwdTg6ta5rBwpjD0gNpT0PNIFIEv641H5qtIOhQXTR83oWuv5nV7_Xx/s320/IMG_20211017_232049.jpg" width="320" /></a></div><br /><p>En el post anterior vimos una versión beta de un programa que sirve para monitorear cual es el rendimiento del PC, y si bien ya era algo funcional, le he hecho algunas mejoras para que resulte más fácil de leer.</p><p>El código está conformado por dos partes, la primera, un programa en C# para windows que se encarga de obtener la información de cual es la carga del CPU en porcentaje, pero además de la RAM también en porcentaje, luego esa información la envía a un puerto al cual conectamos un arduino. La segunda parte consiste en un arduino que toma la información que recibió del programa y la muestra.</p><h3 style="text-align: left;">El programa en C#</h3><div>El código fuente (del formulario principal) es el siguiente:</div><div><br /><pre>using System;
using System.Windows.Forms;
using System.IO.Ports;
namespace CPUMeterToArduino
{
public partial class Form1 : Form
{
System.Diagnostics.PerformanceCounter CPULoad;
System.Diagnostics.PerformanceCounter RAMInUse;
System.Diagnostics.PerformanceCounter AvailableRAM;
private SerialPort Port = new SerialPort();
private void ArduinoMessage(string Message)
{
try
{
Port.Write(Message);
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string DATA = string.Concat(((int)CPULoad.NextValue()).ToString("D3") , "," , PercentRAM(RAMInUse.NextValue() , AvailableRAM.NextValue()).ToString("D3") , "\0");
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
if (indata.Contains("\r\n"))
{
//ArduinoMessage("100,050\0");
ArduinoMessage(DATA);
}
}
Timer TimerMain;
private int PercentRAM(float InUse, float Available)
{
if (Available == 0)
return 100;
else
{
float RamMBInUse = (InUse / 1024 / 1024);
float TotalRam = RamMBInUse + Available;
float Percent = ((RamMBInUse / TotalRam) * 100);
return (int)Percent;
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
RAMInUse = new System.Diagnostics.PerformanceCounter("Memory", "Committed Bytes");
AvailableRAM = new System.Diagnostics.PerformanceCounter("Memory", "Available MBytes");
CPULoad = new System.Diagnostics.PerformanceCounter("Processor", "% Processor Time", "_Total");
TimerMain = new Timer();
TimerMain.Interval = 1000;
TimerMain.Enabled = true;
TimerMain.Tick += new System.EventHandler(TimerMain_Tick);
string[] Ports = SerialPort.GetPortNames();
cboportname.Items.AddRange(Ports);
cboportname.Text = "COM3";
Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
TSSLbl.Text = Port.IsOpen ? "Conectado" : "No conectado";
}
private void TimerMain_Tick(object sender, EventArgs e)
{
PBCPUUsage.Value = (int)CPULoad.NextValue();
int PR = PercentRAM(RAMInUse.NextValue(), AvailableRAM.NextValue());
PBRAMUsage.Value = PR;
this.Text = "Uso de RAM " + PR + "%" + " | CPU: " + PBCPUUsage.Value; ;
NTFYMain.Text = "Uso de RAM " + PR + "%";
TSSLbl.Text = Port.IsOpen ? "Conectado con " + Port.PortName : "No conectado";
}
private void Form1_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
Hide();
NTFYMain.Visible = true;
}
}
private void NTFYMain_MouseDoubleClick(object sender, MouseEventArgs e)
{
Show();
this.WindowState = FormWindowState.Normal;
NTFYMain.Visible = false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (Port.IsOpen == true)
{
Port.Close();
}
NTFYMain.Visible = false;
}
private void BtnConnect_Click(object sender, EventArgs e)
{
try
{
if (Port.IsOpen == false)
{
Port.BaudRate = 9600;
Port.PortName = cboportname.Text;
Port.Open();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BtnDis_Click(object sender, EventArgs e)
{
if (Port.IsOpen == true)
{
Port.Write("Se ha desconectado el cliente\0");
Port.Close();
}
}
private void TSMIRestore_Click(object sender, EventArgs e)
{
NTFYMain_MouseDoubleClick(null, null);
}
private void TSMIExit_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}
</pre><br /></div><div><br /></div><div>Cómo podrás ver el código no sufrió tantos cambios <a href="https://xworkforall.blogspot.com/2021/09/vamos-programar-97.html" target="_blank">por lo que recomiendo que leas el post anterior donde se detalla cada parte</a>, de cualquier forma, lo puedes descargar al final del post.</div><div><br /></div><h3 style="text-align: left;">El código para arduino.</h3><div>La parte para arduino fue la que mas cambio, hay que recordar que hacemos uso de la libreria "LiquidCrystal_I2C.h" y la puedes encontrar en el administrados de librerías de arduino.</div><pre>#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4);
char Texto[80];
char BuffCPU[20];
char BuffRAM[20];
byte BarBeginEmpty[] = {
B11111,
B10000,
B10000,
B10000,
B10000,
B10000,
B10000,
B11111
};
byte BarBeginFull[] = {
B11111,
B10000,
B10111,
B10111,
B10111,
B10111,
B10000,
B11111
};
byte BarSegmentFull[] = {
B11111,
B00000,
B11111,
B11111,
B11111,
B11111,
B00000,
B11111
};
byte BarSegmentEmpty[] = {
B11111,
B00000,
B00000,
B00000,
B00000,
B00000,
B00000,
B11111
};
byte BarEndEmpty[] = {
B11111,
B00001,
B00001,
B00001,
B00001,
B00001,
B00001,
B11111
};
byte BarEndFull[] = {
B11111,
B00001,
B11101,
B11101,
B11101,
B11101,
B00001,
B11111
};
void setup() {
lcd.init();
lcd.createChar(0 , BarBeginEmpty);
lcd.createChar(1 , BarBeginFull);
lcd.createChar(2 , BarEndEmpty);
lcd.createChar(3 , BarEndFull);
lcd.createChar(4 , BarSegmentEmpty);
lcd.createChar(5 , BarSegmentFull);
lcd.backlight();
Serial.begin(9600);
}
void DrawProgressBar(uint8_t Value, uint8_t XLocation, uint8_t YLocation){
uint8_t Progress = map(Value , 1 , 100 , 0 , 20);
lcd.setCursor(XLocation , YLocation);
for (uint8_t i = 0; i < 20; i++)
{
if (i == 0)
{
if (Progress == 0)
lcd.write(0);
else
lcd.write(1);
}
else if (i == 19)
{
if (Progress == 20)
lcd.write(3);
else
lcd.write(2);
}
else
{
if (Progress <= i)
lcd.write(4);
else
lcd.write(5);
}
}
}
void loop() {
int i = 0;
if (Serial.available()) {
while (Serial.available() > 0) {
Texto[i] = Serial.read();
i++;
}
Texto[i] = '\0';
}
String Text(Texto);
int X = Text.substring(0 , 3).toInt();
int Y = Text.substring(5 , 7).toInt();
lcd.setCursor(0 , 0);
//lcd.print(Texto);
sprintf(BuffCPU , "CPU: %03d", X);
sprintf(BuffRAM , "RAM: %03d", Y);
lcd.print(BuffCPU);
lcd.setCursor(0,2);
lcd.print(BuffRAM);
DrawProgressBar(X , 0 , 1);
DrawProgressBar(Y , 0 , 3);
Serial.println("\r\n");
delay(500);
}</pre><p>Para empezar, creamos seis "sprites" que corresponden a los segmentos de la barra de progreso "<i>BarBeginEmpty</i>", "<i>BarBeginFull</i>", "<i>BarSegmentFull</i>", "<i>BarSegmentEmpty</i>", "<i>BarEndEmpty</i>" y "BarEndEmpty", aunque sus nombres pueden resultar auto-explanatorios para algunos, cada uno define cada estado para las diferentes partes de la barra de progreso. Cada uno de los segmentos de la pantalla están conformados por una matriz de 5x8, entonces (y por ahora no entrare en detalles) creamos un array de esa dimensión y para simplificarlo, podemos aprovechar que arduino acepta numero en binario siempre y cuando vayan precedidos por el prefijo "B", ahora observemos la siguiente imagen:</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ5zDuSLbmLSzWjHTQlhQg59r6oQn0Nj2dnoDGhjOdffgw6_JuDeDywOp0sEYcqwEDhC_m3x5g8KFHL7Cb29ZZAFhHAPzO6IN3zy5cDTPPBl3KdYD9YjYbQon3MCjJ-EUsIYY94UFYZEZq/s639/BBFCode.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="639" data-original-width="395" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ5zDuSLbmLSzWjHTQlhQg59r6oQn0Nj2dnoDGhjOdffgw6_JuDeDywOp0sEYcqwEDhC_m3x5g8KFHL7Cb29ZZAFhHAPzO6IN3zy5cDTPPBl3KdYD9YjYbQon3MCjJ-EUsIYY94UFYZEZq/s320/BBFCode.jpg" width="198" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Está es la definición de "<i>BarBeginFull</i>"</td></tr></tbody></table><br /><p>Podemos observar que para formar el sprite, simplemente debemos de decidir si queremos o no usar cada cuadrado de la matriz. Si nos fijamos bien en la imagen, "<i>BarBeginFull</i>" es: "B11111,B10000,B10111,B10111,B10111,B10111,B10000,B11111"</p><p>Por lo tanto podemos usar una tabla e iluminar las celdas para formar el sprite que queramos.</p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgW44iyOXTx0A0To7UcPtnK3eN85oVF5P9rPJLu5xE2XIUa8gsAqlonfSLv9RwhOR8bIGk1WWD5HGhIW7XqVNl53bqkiW6ioDWdiT3Wbf4jgIEKSm_zgvnIQC9Xfr4uDeCJUH14C4c3U2ct/s639/BSF.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="639" data-original-width="395" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgW44iyOXTx0A0To7UcPtnK3eN85oVF5P9rPJLu5xE2XIUa8gsAqlonfSLv9RwhOR8bIGk1WWD5HGhIW7XqVNl53bqkiW6ioDWdiT3Wbf4jgIEKSm_zgvnIQC9Xfr4uDeCJUH14C4c3U2ct/s320/BSF.jpg" width="198" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Definición de "<i>BarSegmentFull</i>"</td></tr></tbody></table><br /><p><br /></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtvwsRnjDRtAEdPXgWTYW2-zB4rAUYyIptVfgk001mO2qF_gznKeCbWj5znUttHjfp71ERLjXRAT-IZNFAvU4yNbSmEbHaEQdKw_A8HvJj0jXtRQQ5AfXddeKlqcT1_09p3t-mVd_Ak7rJ/s639/BSE.jpg" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="639" data-original-width="395" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtvwsRnjDRtAEdPXgWTYW2-zB4rAUYyIptVfgk001mO2qF_gznKeCbWj5znUttHjfp71ERLjXRAT-IZNFAvU4yNbSmEbHaEQdKw_A8HvJj0jXtRQQ5AfXddeKlqcT1_09p3t-mVd_Ak7rJ/s320/BSE.jpg" width="198" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Definición de "<i>BarSegmentEmpty</i>"<br /><br /></td></tr></tbody></table>Para poder hacer uso de los sprites, bastara con usar la función "<i>lcd</i>.<i>createChar</i>()" que recibe cómo parámetros un valor del tipo "byte" que sirve para indicar en cual de los espacios reservados para caracteres personalizados vamos a almacenar nuestro sprite (esto hay de que detallarlo, pero será en otro post, por ahora solo toma en cuenta que los primeros ocho espacios son utilizables, es decir de 0 a 7, después de eso son los caracteres definidos dentro de la propia pantalla, pero cómo ya dije eso es para otro post). El segundo parámetro es el arreglo que contiene os datos de nuestro sprite.<br /><br /><div>Luego tenemos el procedimiento "<i>DrawProgressBar</i>()", este toma tres parámetros. El primero es el valor que tendrá la barra y para esto se asume que se recibirá siempre un valor entre 1 y 100 (para facilitar las cosas), el segundo parámetro sirve para indicar cual será la locación X de la barra, puede ser cualquier valor, pero ya que por lo general las pantallas tienen 16 o 20 espacios, lo recomendable es que el valor este dentro de estos, pero peor aun y es algo que note al escribir esto (y que voy a dejar asi cómo muestra de mi estupidez) es que la barra no se va a adaptar si se usa un valor distinto de 0, si se escribe otro, simplemente aparecerá incompleta. El tercer parámetro es la locación en el eje Y, al igual que el anterior, sirve para indicar la ubicación y puesto que la mayor parte de la pantallas son de 16x2 o 20x4, el parámetro debería de estar entre 0 y 1 para la primera y 0 y 3 para la segunda. Después se toma el parámetro "<i>Values</i>" y se le aplica la función "<i>map</i>" para que el rango este entre 0 y 20 que es el tamaño de la pantalla en mi caso, si tu pantalla es de 16 x 2 por ejemplo, esta línea la debes de modificar (y es la parte que olvide a la hora de escribir el código). Luego, en el ciclo "<i>for</i>", debemos de recorrer de 0 hasta el tamaño de pantalla y comprobar los distintos estados, puesto que la variable "<i>Progress</i>" ya tiene un valor que esta dentro del los limites de la pantalla, bastara con comparar, si el iterador es menor o igual que "<i>Progress</i>" dibujamos el sprite lleno, pero si no es el caso, dibujamos el vacío.</div><div><br /></div><div>Y bien, por ahora es todo, aprovechando que no tome las previsiones necesarias para cuando las pantallas son diferentes a la mía, en los próximos días actualizaré el programa, pero de igual manera, <a href="https://www.dropbox.com/s/p3j97qltjetndce/CPUMeterToArduino.rar?dl=1" target="_blank">el código fuente del programa de C# lo puedes descargar de mi dropbox</a> y <a href="https://www.dropbox.com/s/q8kkd24a52g4cg8/CPUMonitor.rar?dl=1" target="_blank">tambien el código de arduino lo puedes descargar de mismo lugar</a> para que lo modifiques o lo pruebes mientras subo la version corregida.</div><div><br /></div><div>Los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-63756786424982574522021-09-19T13:29:00.002-05:002021-09-19T13:29:44.013-05:00Vamos a programar #97 - Ver el rendimiento del PC usando arduino y C# (beta).<p> Hola de nuevo a todos, hace unos días mientras hacia algunas cosas en la computadora, apague el monitor mientras la PC hacia los suyo, pero al paso de una hora, comencé a prender el monitor cada diez minutos para poder ver si ya había terminado de trabajar, tras cinco encendidos, la computadora finalmente termino de hacer lo que estaba haciendo y proseguí con mi labor. Cómo se me hizo fastidioso apagar y prender el monitor en algo que se que va a tardar mucho, pero que sin embargo no tengo la certeza de cuanto va durar, decidí usar la vieja combinación de arduino y una pantalla LCD.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlRu8PoxREkVhqdEaxpaWKvGL0WdhnxIfjdDqJ7Qus8YJT5gOx6ECFd2haTInJW8gAd-aARiBRr0QKFrVi_t3GkaQTjlJ9ez-GkRkopujblfBpRhzjZk6PAPaVBmB7-sZxqKNLkBjdRjVB/s4000/IMG_20210911_225507.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="3000" data-original-width="4000" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlRu8PoxREkVhqdEaxpaWKvGL0WdhnxIfjdDqJ7Qus8YJT5gOx6ECFd2haTInJW8gAd-aARiBRr0QKFrVi_t3GkaQTjlJ9ez-GkRkopujblfBpRhzjZk6PAPaVBmB7-sZxqKNLkBjdRjVB/s320/IMG_20210911_225507.jpg" width="320" /></a></div><br /><p>La mayor parte de aplicaciones que requieren tiempo para realizar su trabajo, consumen una gran cantidad de recursos, por lo que podemos conectar un arduino, y monitorear cual es la carga para el CPU, usualmente, si la actividad del CPU es mayor a 25%, podemos decir que aun se lleva a cabo un proceso. Para poder mostrar la información, usaremos de nueva cuenta un arduino y la pantalla LCD, tal y cómo lo hicimos con "<a href="https://xworkforall.blogspot.com/2019/05/vamos-programar-73.html" target="_blank">Message Sender" (o la aplicación para ver cual era la ventana activa)</a>. En este caso, un programa en C# obtenía la información y la enviaba a arduino para poder desplegarla, cosa que también haremos ahora.</p><h3 style="text-align: left;">La aplicación en C#</h3><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKq_pXYtfVLAfUBDvgivK6Vfh3GTaGTNdGHvgeCVqUZxZAy9GetjNHo9Sb9-4xmTzI_komnPGcMhLGIZ9FSoqK4tfUzQiogrC5hyIRQVHy7ZVo8VDzVh35H5aGaTN1SjfbpO2RgrN0w5QI/s290/Captura+de+pantalla+2021-09-19+11.45.35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="246" data-original-width="290" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKq_pXYtfVLAfUBDvgivK6Vfh3GTaGTNdGHvgeCVqUZxZAy9GetjNHo9Sb9-4xmTzI_komnPGcMhLGIZ9FSoqK4tfUzQiogrC5hyIRQVHy7ZVo8VDzVh35H5aGaTN1SjfbpO2RgrN0w5QI/s0/Captura+de+pantalla+2021-09-19+11.45.35.png" width="290" /></a></div><br /><div><br /></div><p>El código en C# que sirve para enviar la información del uso del CPU a arduino es el que sigue.</p><pre><p>using System;
using System.Windows.Forms;
using System.IO.Ports;
namespace CPUMeterToArduino
{
public partial class Form1 : Form
{
System.Diagnostics.PerformanceCounter CPULoad;
System.Diagnostics.PerformanceCounter RAMInUse;
System.Diagnostics.PerformanceCounter AvailableRAM;
private SerialPort Port = new SerialPort();
private void ArduinoMessage(string Message)
{
try
{
Port.Write(Message);
}
catch (Exception e)
{
MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string DATA = string.Concat("CPU ", CPULoad.NextValue().ToString("F2"), "%", " Ram ",
PercentRAM(RAMInUse.NextValue(), AvailableRAM.NextValue()), "%");
SerialPort sp = (SerialPort)sender;
string indata = sp.ReadExisting();
if (indata.Contains("\r\n"))
{
ArduinoMessage(DATA);
}
}
Timer TimerMain;
private int PercentRAM(float InUse, float Available)
{
if (Available == 0)
return 100;
else
{
float RamMBInUse = (InUse / 1024 / 1024);
float TotalRam = RamMBInUse + Available;
float Percent = ((RamMBInUse / TotalRam) * 100);
return (int)Percent;
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
RAMInUse = new System.Diagnostics.PerformanceCounter("Memory", "Committed Bytes");
AvailableRAM = new System.Diagnostics.PerformanceCounter("Memory", "Available MBytes");
CPULoad = new System.Diagnostics.PerformanceCounter("Processor", "% Processor Time", "_Total");
TimerMain = new Timer();
TimerMain.Interval = 1000;
TimerMain.Enabled = true;
TimerMain.Tick += new System.EventHandler(TimerMain_Tick);
string[] Ports = SerialPort.GetPortNames();
cboportname.Items.AddRange(Ports);
cboportname.Text = "COM3";
Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
private void TimerMain_Tick(object sender, EventArgs e)
{
PBCPUUsage.Value = (int)CPULoad.NextValue();
int PR = PercentRAM(RAMInUse.NextValue(), AvailableRAM.NextValue());
PBRAMUsage.Value = PR;
this.Text = "Uso de RAM " + PR + "%";
NTFYMain.Text = "Uso de RAM " + PR + "%";
}
private void Form1_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
Hide();
NTFYMain.Visible = true;
}
}
private void NTFYMain_MouseDoubleClick(object sender, MouseEventArgs e)
{
Show();
this.WindowState = FormWindowState.Normal;
NTFYMain.Visible = false;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (Port.IsOpen == true)
{
Port.Close();
}
NTFYMain.Visible = false;
}
private void BtnConnect_Click(object sender, EventArgs e)
{
try
{
if (Port.IsOpen == false)
{
Port.BaudRate = 9600;
Port.PortName = cboportname.Text;
Port.Open();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void BtnDis_Click(object sender, EventArgs e)
{
if (Port.IsOpen == true)
{
Port.Write("Se ha desconectado el cliente");
Port.Close();
}
}
private void TSMIRestore_Click(object sender, EventArgs e)
{
NTFYMain_MouseDoubleClick(null, null);
}
private void TSMIExit_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}
</p><div><br /></div></pre><p>Para poder utilizarlo requerimos de los siguientes controles:</p><p></p><ul style="text-align: left;"><li>2 ProgressBar</li><ul><li>PBCPUUsage</li><li>PBRAMUsage</li></ul><li>1 ComboBox</li><ul><li>cboportname</li></ul><li>2 Button</li><ul><li>BtnConnect</li><li>BtnDis</li></ul><li>1 NotifyIcon</li><ul><li>NTFYMain</li></ul><li>1 ContextMenuStrip</li><ul><li>CMSTray</li></ul></ul>Para poder revisar cual es el rendimiento, deponemos de algunos objetos de "<i>System.Diagnostics.PerformanceCounter</i>" que fueron creados para poder obtener información del sistema, en este caso creamos tres objetos de este tipo; "<i>CPULoad</i>", "<i>RAMInUse</i>" y "<i>AvailableRAM</i>", para poder utilizarlos, debemos de usar los constructores, este recibe tres parámetros, el primero del tipo "<i>string</i>" que es el nombre de la categoría, que para "<i>RAMInUse</i>" y "<i>AvailableRAM</i>" es "<i>Memory</i>" y para "<i>CPULoad</i>" es "<i>Processor</i>". En este caso existen mas categorías que podrían ser de utilidad, para poder ver cuales son las disponibles, podemos hacer uso de la función "<i>PerformanceCounterCategory.GetCategories()</i>" (pero eso lo veremos en otro post). Para el segundo parámetro, usamos en "<i>RAMInUse</i>" "<i>Committed Bytes</i>", para "<i>AvailableRAM</i>" "<i>Available MBytes</i>" y para "<i>CPULoad</i>" "<i>% Processor Time</i>". Cómo "<i>RAMInUse</i>" y "<i>AvailableRAM</i>" no cuentan con una sub-categoría, el tercer parámetro lo dejamos vacío, pero para "<i>CPULoad</i>" usamos "<i>_Total</i>".<div><br /></div><div>Una vez creados los objetos, al momento de inicializar el formulario, crearemos un temporizador "<i>TimerMain</i>" con la propiedad "Interval" igual a "<i>1000</i>", la propiedad "<i>Enabled</i>" igual a "true" y para el evento "<i>Tick</i>" usaremos "<i>System.EventHandler</i>" con parámetro "<i>TimerMain_Tick</i>". Cada vez que se produzca el evento "<i>Tick</i>" de "<i>TimerMain</i>", es cuando se va a realizar la lectura de los valores usando las funciones "<i>NextValue</i>()" de los objetos "<i>RAMInUse</i>", "<i>AvailableRam</i>" y "<i>CPULoad</i>", esta función devuelve un valor del tipo "<i>float</i>" que en el caso de "<i>RAMInUse</i>" corresponde al numero de bytes utilizados de la memoria RAM, en el caso de "<i>AvailableRAM</i>"; representa la cantidad de mega bytes disponibles de la memoria RAM y finalmente en "<i>CPULoad</i>" representa el porcentaje de uso del procesador.</div><div><br /></div><div>También creamos una función del tipo "<i>int</i>" que sirve para calcular el porcentaje de un número, para hacerlos, recibe dos parámetros del tipo "<i>float</i>", "<i>InUse</i>" y "<i>Available</i>", en este caso cómo lo vamos a usar para llenar la barra de progreso en proporción a la cantidad de memoria en uso, al solo obtener la cantidad de RAM usada y la disponible no usada, para saber cual es la cantidad total disponible, debemos de sumarlas, pero cómo la cantidad en uso está en bytes y la disponible está en mega bytes, debemos de convertir en este caso decidí convertir los bytes a mega bytes y para eso, solo basta con dividir entre 1024 y nuevamente entre 1024, una vez que tenemos los valores, simplemente aplicamos la formula del porcentaje y devolvemos el valor cómo resultado.</div><div><br /></div><div>Los demás procedimientos son usados para crear el puerto y manejar la información y son tal cual los usamos en <i>MessageSender</i>.</div><div><br /></div><h3 style="text-align: left;">El código en arduino.</h3><div>El código para arduino es el siguiente, pero es igual al de <i>"<a href="https://xworkforall.blogspot.com/2019/05/vamos-programar-73.html" target="_blank">MessageSender</a></i>"</div><div><br /></div><div><pre><br/>#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display
char Texto[80];
void setup() {
lcd.init();
lcd.backlight();
Serial.begin(9600);
}
void loop() {
int i = 0;
if (Serial.available()) {
while (Serial.available() > 0) {
Texto[i] = Serial.read();
i++;
}
Texto[i] = '\0';
}
lcd.clear();
lcd.setCursor(0 , 0);
lcd.print(Texto);
Serial.println("\r\n");
delay(1000);
}
</pre></div><div><br /></div><div><br /></div><div>Y bien, por ahora es todo, en el siguiente post publicaré la version final y publicaré todo el código fuente para que lo modifiques a tu gusto, <a href="https://www.dropbox.com/s/p3j97qltjetndce/CPUMeterToArduino.rar?dl=1" target="_blank">por ahora puedes bajar el ejecutable para que lo pruebes</a> y basta con copiar y compilar el código para arduino.</div><div><br /></div><div>Los leo luego.</div><div><br /></div><div>"IMOSIN"</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-26101578540797371172021-07-11T15:24:00.004-05:002021-07-11T15:24:51.539-05:00Vamos a programar #97 - Ver el contenido de la memoria EEPROM de arduino.<p> Hola de nuevo a todos, el día de hoy vamos a continuar con un poco mas de Arduino, pero ma precisamente de la memoria EEPROM.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinknJyTGfKDMMWDKustGv4E24x7Kl3d6zuz__pm-cas_mJaK3ykEFLBuHdNZdtYBm9wGWM3mGZ4mGYbdoQBKyk-csUlEnIVUQ1ZDiflWCCW8SvNrqJbE02OZkz7hjR5sOxS_RW93eMf5MJ/s952/Captura+de+pantalla+2021-07-11+14.55.38.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="853" data-original-width="952" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinknJyTGfKDMMWDKustGv4E24x7Kl3d6zuz__pm-cas_mJaK3ykEFLBuHdNZdtYBm9wGWM3mGZ4mGYbdoQBKyk-csUlEnIVUQ1ZDiflWCCW8SvNrqJbE02OZkz7hjR5sOxS_RW93eMf5MJ/s320/Captura+de+pantalla+2021-07-11+14.55.38.png" width="320" /></a></div><br /><p><br /></p><p>En post anteriores hemos visto que las placas de arduino poseen una memoria que no es volátil y que sirve para guardar cosas, dependiendo del modelo, tendremos hasta 4096 bytes de espacio, si es poco lo que se va a guardar nos sirve. Recientemente he estado trabajando en un proyecto que hace uso de la memoria, pero surgió un problema, me interesa saber cual es el contenido y me resulta molesto llamar de una por una las variables para ver su contenido.</p><p>Para resolverlo, decidí hacer un visualizador de memoria al mas puro estilo XVI32 (pero mucho mas chafa), el chiste es tener una forma de visualizar un poco la información. Para lograrlo use el siguiente código.</p><div><pre>// Mostrar el contenido de la memoria EEPROM de arduino al estilo XVI32
#include <EEPROM.h>
uint8_t PrintableCharacter(uint8_t TheChar){
if (TheChar < 31 || TheChar > 127)
return 46;
else
return TheChar;
}
void setup(){
Serial.begin(115200);
while (!Serial) {
;
}
for (int Block = 0 ; (Block * 16) <= (EEPROM.length() - 16) ; Block++){
uint8_t Values[16];
for (uint8_t CurrentValue = 0 ; CurrentValue <= 16 ; CurrentValue++){
int address = (Block * 16) + CurrentValue;
Values[CurrentValue] = EEPROM.read(address);
}
char buf[100];
sprintf(buf, "%04X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c %02c",
Block * 16,
Values[0] , Values[1] , Values[2] , Values[3] , Values[4] , Values[5] , Values[6] , Values[7] ,
Values[8] , Values[9] , Values[10] , Values[11] , Values[12] , Values[13] , Values[14] , Values[15] ,
PrintableCharacter(Values[0]) , PrintableCharacter(Values[1]) , PrintableCharacter(Values[2]) , PrintableCharacter(Values[3]) , PrintableCharacter(Values[4]) , PrintableCharacter(Values[5]) , PrintableCharacter(Values[6]) , PrintableCharacter(Values[7]) ,
PrintableCharacter(Values[8]) , PrintableCharacter(Values[9]) , PrintableCharacter(Values[10]) , PrintableCharacter(Values[11]) , PrintableCharacter(Values[12]) , PrintableCharacter(Values[13]) , PrintableCharacter(Values[14]) , PrintableCharacter(Values[15]));
Serial.println(buf);
}
}
void loop(){
}</pre></div><br /><p>El programa consta de una función (mas las dos por default que usa Arduino) la primera de ellas del tipo "<i>uint8_t</i>" es "<i>PrintableCharacter</i>()" y su función es la determinar si el numero que se la pasa cómo parámetro esta dentro del rango de caracteres imprimibles, si el numero esta dentro del rango 31 ~ 127, la función regresa el mismo numero que se paso cómo parámetro, en caso contrario regresa el numero 46 que en los valores ASCIIrepresenta al punto ".".</p><p>Dentro del procedimiento <i>setup</i>(), simplemente hacemos un ciclo "<i>for</i>" que recorrerá desde cero hasta "<i>EEPROM.length() - 16</i> " pero cómo queremos que cuente de 16 en 16, cada línea consistirá de 16 bytes que se leerán y se almacenaran en un buffer llamado "<i>Values</i>[]", una vez que se ha leído cada bloque, se desplegara usando el siguiente formato: cuatro números hexadecimales para la dirección, dos números hexadecimales para cada valor y además llamaremos a la función "<i>PrintableCharacter</i>" para saber si debemos de mostrar un punto o el valor ASCII del numero.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDxwvQnWF390rwQ03dMMh3fum8aSS15fkvA0c-5ltttSLFoIB9WkMjQ4WWNqA0vFXzd-1qiDBD2I32UMECONIHoSjhsgPdAsz2liTVLPHdM2CuKS9Qozd3s2bdUeiBSpQMtMoUx-loOzrO/s877/Captura+de+pantalla+2021-07-11+15.23.08.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="853" data-original-width="877" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDxwvQnWF390rwQ03dMMh3fum8aSS15fkvA0c-5ltttSLFoIB9WkMjQ4WWNqA0vFXzd-1qiDBD2I32UMECONIHoSjhsgPdAsz2liTVLPHdM2CuKS9Qozd3s2bdUeiBSpQMtMoUx-loOzrO/s320/Captura+de+pantalla+2021-07-11+15.23.08.png" width="320" /></a></div><br /><p><br /></p><p>Cómo podrás observar, funciona y ahora podremos ver de manera mas sencilla el contenido de la memoria, hay que recordar que esta es independiente del espacio donde se almacena el programa por lo que podemos ejecutar este y una vez visualizados los datos deseados, volver a instalar el programa que usemos para escribir en la EEPROM. <a href="https://www.dropbox.com/s/cji95aljiahbpwk/XviArduino.rar?dl=1" target="_blank">Cómo de costumbre, puedes bajar el código de mi dropbox para probarlo</a>. En el siguiente post, veremos cómo editar la memoria desde la computadora y repasaremos cómo es que arduino almacena los tipos y las variables en memoria.</p><p>Los leo luego.</p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-22474034389434689152021-06-21T14:40:00.002-05:002021-06-21T14:40:16.219-05:00Cumplimos cinco años!!!<p> Hola de nuevo a todos, el día de hoy solo escribo para recordarles que un día cómo hoy pero de hace cinco años, nació el blog de Xwork. He cierto que ha sido altas y bajas, pero al menos seguimos en la lucha. Es cierto que la cantidad de post ha bajado, pero sigo firme en mi promesa de sacar al menos uno al mes (de baja calidad pero sin ser clic bait).</p><p>El aspecto del blog se ha renovado y a diferencia de la vez anterior, todos los errores han sido solucionados por lo que hay que darle una oportunidad (pero si el pueblo lo demanda lo cammbio de nuevo).</p><p>Aun hay mucho que podemos aprender asi que trataré de seguir hasta que haya un impedimento para hacerlo. Por ahora es todo, sigan vivos y felices y nos leeremos todavía.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAT124roLpKMoao1HO7t3Q57ZCYHF1tIqqqBLFfVTS_8of7cQEoIfEN75qtqJ4x19VYjJZ5ND6UoK2Hls4JH_hPuFSw4hBIrpIWnvzG3kwXsdPCHTxM44JJZSJNoB9X-l4tPQync9Wta5j/s1920/X5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1080" data-original-width="1920" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAT124roLpKMoao1HO7t3Q57ZCYHF1tIqqqBLFfVTS_8of7cQEoIfEN75qtqJ4x19VYjJZ5ND6UoK2Hls4JH_hPuFSw4hBIrpIWnvzG3kwXsdPCHTxM44JJZSJNoB9X-l4tPQync9Wta5j/w400-h225/X5.png" width="400" /></a></div><br /><p><br /></p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-71985707585149573422021-06-13T16:13:00.001-05:002021-06-13T16:19:21.479-05:00Vamos a programar #96 - Guardando configuraciones en la EEPROM de arduino.<p> Hola de nuevo a todos, el día de hoy vamos a continuar con mas sobre la EEPROM de arduino. EN el post anterior vimos cuales son las funciones disponibles para trabajar con ella (y nada mas) pero no vimos ningún ejemplo practico.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdOzOgrI4_Apt-5iFwJa9m-gnKPIc4pp0RxBe5Y3f6zSXfoAszu0kcx4AxnZcMBqUgJ1ao5b1lWn8tg0JjxiP2hjzAQgqy2lLQ9eVxE-M6XLAweJ2xDT7i7eb9h4MWwKKJd0kvAEbn_Ts8/s1280/floppy-disc-23343_1280.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1214" data-original-width="1280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdOzOgrI4_Apt-5iFwJa9m-gnKPIc4pp0RxBe5Y3f6zSXfoAszu0kcx4AxnZcMBqUgJ1ao5b1lWn8tg0JjxiP2hjzAQgqy2lLQ9eVxE-M6XLAweJ2xDT7i7eb9h4MWwKKJd0kvAEbn_Ts8/s320/floppy-disc-23343_1280.png" width="320" /></a></div><br /><p><br /></p><p>Para el ejemplo del día de hoy vamos a suponer que queremos guardar una configuración simple donde solo tenemos dos ajustes. El primero será una cadena de texto de longitud 5 y la segunda un entero (en arduino por default los enteros son de 16 bits para los modelos UNO). Para eso podemos crear código cómo el que sigue.</p><div><pre><p>#include <EEPROM.h>
//Para limpiar la memoria, solo deberia de ser usado una vez
//Se puede quitar los comentarios, compilar y subir a arduino y despues del primer uso
//Eliminarse o comentarse de nuevo para no gastar la vida util de la memoria EEPROM
// for (int i = 0 ; i < EEPROM.length() ; i++) {
// EEPROM.write(i, 0);
// }
int CheckData = EEPROM.get(0, CheckData);
struct MySettings{
char MyName[20];
int MyValue;
};
void setup() {
int Address = 0;
Serial.begin(9600);
while (!Serial) {
;
}
if (CheckData > -1)
{
Serial.println("Al parecer no se guardado la configuracion antes");
EEPROM.put(Address , int(-1));
Address += sizeof(CheckData);
MySettings SavSettings = {
"XWORK",
88
};
EEPROM.put(Address , SavSettings);
Serial.println("Guardado");
}else{
Serial.println("Parece haber datos validos de configuracion");
Address += sizeof(CheckData);
MySettings SavSettings;
EEPROM.get(Address , SavSettings);
Serial.println(SavSettings.MyName);
Serial.println(SavSettings.MyValue);
Serial.println("Cargado");
}
}
void loop() {
}</p></pre></div><p>Antes que nada, hay que tomar un par de consideraciones. A pesar de que tenemos un numero de "seguridad", lo ideal seria limpiar la memoria EEPROM una vez antes de hacer uso de ella, así sabríamos que es lo que contiene antes de hacer uso de ella, si no lo hacemos, no podemos asegurar que es lo que hay y a la hora de revisar podríamos tener un valor válido para "<i>CheckData</i>" pero no significaría que existan datos útiles para cargar. En los ejemplos de arduino existe un sketch llamado "eeprom_clear" y su función es esa, la de definir todos los bytes a 0. La otra consideración es que usamos directamente las funciones "<i>EEPROM.get()</i>" y "<i>EEPROM.put()</i>" y la ventaja de estas es que acepta tipos y estructuras, pero sobre todo, solo actualiza los valores si son diferentes a los que se encuentran guardados (lo que incrementa el tiempo de vida de la EEPROM).</p><p>Ahora si veamos cómo es que funciona el código. Para empezar, primero creamos una variable llamada "<i>CheckData</i>" y su valor será lo que este guardado en la memoria EEPROM en la dirección 0. Hay que recordar que la función "<i>EEPROM.get()</i>" toma dos parámetros, el primero será la dirección en la cual va a leer y el segundo el tamaño de los datos en este caso espera un tipo y "<i>CheckData</i>" es del tipo "<i>int</i>". Una vez que tenemos ese valor, creamos una estructura llamada "<i>MySettings</i>" que contendrá dos campos, el primero un arreglo del tipo "<i>char</i>" con una longitud de 20 llamado "<i>MyName</i>" y el segundo un entero llamado "<i>MyValue</i>" del tipo "<i>int</i>".</p><p>Dentro de la función "<i>setup</i>()", lo primero que hacemos es revisar el valor de "<i>CheckData</i>", esta variable la usaremos cómo comprobador, en est caso comprobamos que su valor sea mayor que 0, si es así, asumimos que no hemos guardado los datos antes, por lo que procedemos a hacerlo. Cómo lo primero que hicimos fue cargar el número de comprobación "<i>CheckData</i>", entonces es lo primero que hacemos es guardar esa variable con un valor de -1 en la direccion 0 (para este momento la variable "<i>Address</i>" es 0), una vez que se escribió, hay que movernos. Un poco mas arriba mencioné que Arduino UNO usa dos bytes para guardar los enteros, pero cómo es poco practico movernos "de byte en byte", usaremos la función "<i>sizeof</i>()", esta recibe cómo parámetro un tipo y devuelve su longitud en bytes, entonces lo que hacemos, es simplemente asignar el resultado de la llamada a la función "<i>Address</i>".</p><p>Una vez que esta guardado el número de comprobación, podemos guardar nuestros datos, para eso primero creamos una variable de nuestra estructura, en este caso "<i>SavSettings</i>" y le asignamos valores. Con los valores asignados, procedemos a guardarlos usando "EEPROM.put()". Cómo primer parámetro (dirección en donde guardar), le asignaremos la variable "<i>Address</i>" y cómo segundo parámetro, "<i>SavSettings</i>".</p><p>Si la comprobación dice que ya hay valores validos, simplemente leemos los datos que estén después de "<i>CheckData</i>" usando "EEPROM.get()" con parámetros "<i>Address</i>" para la dirección y "<i>SavSettings</i>" cómo el tipo que se va a leer. AL finalizar la lectura, simplemente los ponemos en el monitor serie y los mostramos.</p><p>Ahora solo a manera de ejemplo, veamos el caso para guardar dos campos diferentes.</p><p><span style="font-family: monospace;"><span style="white-space: pre;">#include <EEPROM.h>
<div><pre><br/>
//Para limpiar la memoria, solo deberia de ser usado una vez
//Se puede quitar los comentarios, compilar y subir a arduino y despues del primer uso
//Eliminarse o comentarse de nuevo para no gastar la vida util de la memoria EEPROM
// for (int i = 0 ; i < EEPROM.length() ; i++) {
// EEPROM.write(i, 0);
// }
int CheckData = EEPROM.get(0, CheckData);
struct MySettings{
char MyName[20];
int MyValue;
};
void setup() {
int Address = 0;
Serial.begin(9600);
while (!Serial) {
;
}
if (CheckData > -1)
{
Serial.println("Al parecer no se guardado la configuracion antes");
EEPROM.put(Address , int(-1));
Address += sizeof(CheckData);
MySettings SavSettings = {
"XWORK",
88
};
EEPROM.put(Address , SavSettings);
Address += sizeof(MySettings);
MySettings SavSettings2 = {
"MPM88",
90
};
EEPROM.put(Address , SavSettings2);
Serial.println("Guardado");
}else{
Serial.println("Parece haber datos validos de configuracion");
Address += sizeof(CheckData);
MySettings SavSettings;
MySettings SavSettings2;
EEPROM.get(Address , SavSettings);
Serial.println("Datos 1");
Serial.println(SavSettings.MyName);
Serial.println(SavSettings.MyValue);
Address += sizeof(MySettings);
EEPROM.get(Address , SavSettings2);
Serial.println("Datos 2");
Serial.println(SavSettings2.MyName);
Serial.println(SavSettings2.MyValue);
Serial.println("Cargado");
}
}
void loop() {
}<br/>
</pre></div>
</span></span></p><div><br /></div><p>Lo importante del ejemplo anterior es que para leer el segundo campo, simplemente debemos de hacer uso de "<i>sizeof</i>()" pasando cómo parámetro la estructura "<i>MySettings</i>" para establecer el inicio de la siguiente dirección.</p><p>Y bien, por ahora es todo. <a href="https://www.dropbox.com/s/aazbaceii9zqpn4/EEPROMSettings.rar?dl=1" target="_blank">Cómo de costumbre, puedes bajar el código de mi dropbox para probarlo</a>.</p><p>Los leo luego.</p>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0tag:blogger.com,1999:blog-4516612096157502273.post-80980480488261651292021-06-06T17:47:00.000-05:002021-06-06T17:47:09.596-05:00Vamos a programar #95 - Arduino y su EEPROM<p> Hola de nuevo a todos, el dia de hoy vamos a ver cómo usar la memoria EEPROM que viene incluida en la mayoría de los Arduinos.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJeOMwQt2Tte8cvFEB29OeX1XuvUUlusyD3CS6Q9HWU0stUjQmtSjdHUHLdWwXEZcKfR7ajWLa7dQGii0hC4osloTzgdwk_e_e7EszCOuTSS4aav-dBNkFMAb1dy9rn5OoXPcY3I6KaJZE/s2048/2016-07-12+15.58.12.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1536" data-original-width="2048" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJeOMwQt2Tte8cvFEB29OeX1XuvUUlusyD3CS6Q9HWU0stUjQmtSjdHUHLdWwXEZcKfR7ajWLa7dQGii0hC4osloTzgdwk_e_e7EszCOuTSS4aav-dBNkFMAb1dy9rn5OoXPcY3I6KaJZE/s320/2016-07-12+15.58.12.jpg" width="320" /></a></div><br /><p>EL otro día mientras trabajaba en uno de los proyectos que voy a publicar cuando el blog cumpla 5 años. De nueva cuenta quería guardar las configuraciones para poder retornarlas posteriormente, al crear un archivo de prueba con la mayoría de las configuraciones que planeo usar, el archivo difícilmente pesaba 120 bytes. Suponiendo que la mayoría de las memorias disponibles al día de hoy tienen una capacidad de 4GB (4,000,000,000 bytes), eso significaría que podría guardar unos 33 millones de archivos de configuración.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoIXsOHTJ2USgSO4Z98Qzt9EYtATk5PqEuv0tS8RFiYFms-0lweEB3eFG6k4oAmyddGFPMGiqqZg8SMmZdwf8ikyWuMPMAB3JPrTWzbOuu22KXjIC7-IkUdy3kPdKHKZpkvCUGXXW-YM1V/s506/Captura+de+pantalla+2021-06-06+15.29.15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="506" data-original-width="377" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjoIXsOHTJ2USgSO4Z98Qzt9EYtATk5PqEuv0tS8RFiYFms-0lweEB3eFG6k4oAmyddGFPMGiqqZg8SMmZdwf8ikyWuMPMAB3JPrTWzbOuu22KXjIC7-IkUdy3kPdKHKZpkvCUGXXW-YM1V/s320/Captura+de+pantalla+2021-06-06+15.29.15.png" /></a></div><br /><p>El almacenamiento es relativamente barato, por lo que no tendría ningún problema en usar una tarjeta de memoria de esa capacidad, pero desde el punto de vista de lo optimo; es un desperdicio. Por eso me decidí a usar la memoria que viene incluida con arduino. Pero antes que nada , que es una memoria EEPROM.</p><p></p><blockquote> EEPROM o E²PROM son las siglas de Electrically Erasable Programmable Read-Only Memory. Es un tipo de memoria ROM que puede ser programada, borrada y reprogramada eléctricamente. <a href="https://es.wikipedia.org/wiki/EEPROM" target="_blank">Wikipedia/EEPROM</a></blockquote><p> EN arduino la podemos usar para guardar las configuraciones ya que; dependiendo del modelo; nos va a dar unos cuantos bytes. Para los modelos basados en el ATMEGA328P; tenemos 1024 bytes, para el ATMEGA168; 512 bytes, para los modelos ATMEGA1280 y ATMEGA2560; 4096 bytes.</p><p>Para poder usarla, debemos de incluir la libreria "<i>EEPROM.h</i>" que contiene las siguientes funciones:</p><p></p><ul style="text-align: left;"><li><i>EEPROM.Clear()</i>: Limpia la memoria llenandola con ceros.</li><li><i>EEPROM.Read(Address)</i>: Lee la memoria y regresa el valor del byte en la dirección marcada por el parámetro "<i>Address</i>".</li><li><i>EEPROM.Write(Address, Value)</i>:Escribe un valor en la memoria, El valor será el parámetro "<i>Value</i>" y lo hará en la dirección "<i>Address</i>"</li><li><i>EEPROM.Get(Address,Data)</i>: Lee la memoria y regresa el valor en la dirección "<i>Address</i>". El parámetro "<i>Data</i>", indica un tipo o estructura y será leída cada vez (por ejemplo si asignamos a Data un tipo "<i>int</i>", leerá todos los bytes que lo conforman).</li><li><i>EEPROM.Put(Address,Data)</i>: Escribe en la memoria en la dirección "<i>Address</i>" los datos en "<i>Data</i>", puede ser cualquier tipo de los soportados o una estructura. Al igual que <i>EEPROM.Get()</i> se escribirán todos los bytes que conformen al tipo o estructura..</li><li><i>EEPROM.Update(Address, Value)</i>: Actualiza el valore de "<i>Value</i>" en "<i>Address</i>" solo si el valor es distinto al existente (Solo un byte) </li></ul>Con las funciones anteriores podemos leer y escribir en la memoria EEPROM de arduino, solo que hay que considerar que la memoria tiene un ciclo de vida finito, aproximadamente cien mil veces es lo que se puede escribir (para leer no hay limite), por lo que solo es recomendable usarse para valores que no cambiaran tanto, para el caso que la necesito, difícilmente se cambiaran una vez al día por lo que esta garantizado que no se echara a perder.<div><br /></div><div>Y bien, por ahora es todo, en el siguiente post veremos un ejemplo de como usar la mayoria de las funciones y cuales ofrecen ventajas a la hora de manejar la memoria.</div><div><br /></div><div>Los leo luego.</div>Hugo G.http://www.blogger.com/profile/09765372319325193717noreply@blogger.com0