Vamos a programar #85 - VEncoder 2.1
Hola de nuevo a todos, el día de hoy vamos a ver una pequeña actualizacion en el código de VEncoder, ahora pasando de la version 2.0.1 a la version 2.1.
La semana pasada vimos cómo hacer un par de "cosillas" en FFMpeg, de forma ideal, deberíamos de ser capaces de aplicarlas en VEncoder, pero aquí es donde surge el dilema. Tratar de aumentar las opciones, también aumentaría la complejidad del programa y la idea principal (y para la cual fue hecha VEncoder), fue tratar de reducir lo complejo y poder convertir los archivos usando pocos clics. Para mantener un poco el balance entre la usabilidad y la complejidad, la única opción que encontré fue abrir la posibilidad de usar parámetros adicionales. Antes de continuar, veamos el código de VENcoder.
Las funciones que han cambiado son "BuildVideoCommand()" y "BuildAudioCommand()", además se ha agregado la función "BuildExtraCommands(). Las primeras dos funciones ya estaban hechas y solo se actualizaron un poco.
Pero antes, cual es la idea? si el chiste era crear una interfaz para que el usuario no tuviera que escribir nada; y así es, la idea de usar los parámetros extra, es que los usuarios que requieran hacer algo que no está programado en VEncoder, puedan hacerlo y aquellos que requieran algo "más", también puedan hacerlo. Si no se usa ningún parámetro adicional, el programa funcionara cómo venia haciéndolo, pero el hecho de poder "extender" la usabilidad, abre la opción a que la gente experimente. Una vez dicho lo anterior, continuemos con la explicacion.
Las funciones "BuildAudioCommand()" y "BuildVideoCommand()"; ambas recibían parámetros del tipo "string" y su objetivo era construir la linea de comandos. Para hacer uso de los parámetros adicionales, simplemente agregaremos al final de los parámetros que ya teníamos y las funciones devuelven la linea de comandos más los parámetros adicionales si es que se agregan.
La función "BuildExtraCommands()" simplemente toma el texto de una caja de texto y lo agrega al final de la linea de comandos (al igual que las anteriores, si está vacía no pasa nada).
Para agregar los parámetros adicionales, ahora se ha agregado una nueva pestaña llamada "Parámetros Adicionales", y tiene un conjunto de subpestañas en la cual es posible agregar los parámetros.
Tomemos cómo ejemplo las siguiente linea de comandos:
Ahora, si agregamos en el apartado de Video de los parametros adicionales "-tune animation" y en el apartado general "-t 600 -ss 140", la linea de comados resultante será la siguiente:
Cómo podras observar, ahora funcionan otras funciones, si quieres saber cuales están disponibles para el formato mp4 usando el codificador X264, puesde ver la guia de la buena codificacion (en inglés).
A partir de ahora podemos hacer uso extensivo de VEncoder, pero eso trae un par de detalles que aun se deben de resolver, pero el problema es el mismo, cómo aumentar la funcionalidad sin aumentar la complejidad. Cómo de costumbre el código lo puedes descargar de mi dropbox para que lo uses y lo modifiques. Aun queda trabajo pendiente, pero por ahora es todo.
Los leo luego.
La semana pasada vimos cómo hacer un par de "cosillas" en FFMpeg, de forma ideal, deberíamos de ser capaces de aplicarlas en VEncoder, pero aquí es donde surge el dilema. Tratar de aumentar las opciones, también aumentaría la complejidad del programa y la idea principal (y para la cual fue hecha VEncoder), fue tratar de reducir lo complejo y poder convertir los archivos usando pocos clics. Para mantener un poco el balance entre la usabilidad y la complejidad, la única opción que encontré fue abrir la posibilidad de usar parámetros adicionales. Antes de continuar, veamos el código de VENcoder.
namespace VEncoder_2
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Resources;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using System.Xml;
using MediaInfoNET;
using System.Text;
/// <summary>
/// Formulario principal de VEncoder 2
/// </summary>
public partial class FrmMain : Form
{
public FrmMain()
{
InitializeComponent();
}
#region Declaraciones
private bool IsConvertingSomething = false;
private bool RequestForCancel = false;
private int ElapsedTimeGlobal = 0;
private int ElapsedTimeCurrent = 0;
#endregion
#region Funciones
private void SaveProfile(string XmlPath, string ProfileName)
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.IndentChars = "\t";
using (XmlWriter XWriter = XmlWriter.Create(XmlPath, settings))
{
XWriter.WriteStartDocument();
XWriter.WriteComment("Archivo de preset para VEncoder 2");
XWriter.WriteStartElement("Perfil");
XWriter.WriteAttributeString("v", "1");
XWriter.WriteAttributeString("appname", "VEncoder2");
XWriter.WriteElementString("Name", ProfileName);
XWriter.WriteElementString("WH", CboResolucion.Text);
XWriter.WriteElementString("VBit", TXTVBit.Text);
XWriter.WriteElementString("Ratio", CBORatio.Text);
XWriter.WriteElementString("Profile", CBOProfile.Text);
XWriter.WriteElementString("Nivel", CboLevel.Text);
XWriter.WriteElementString("ASample", CboFreq.Text);
XWriter.WriteElementString("ABit2", CBOBitsA.Text);
XWriter.WriteElementString("Channel", CBOAChannels.Text);
//XWriter.WriteElementString("ExtraParams", TXTExtraparams.Text);
XWriter.WriteEndElement();
XWriter.WriteEndDocument();
}
}
/// <summary>
/// Carga un archivo XML con los ajustes para realizar la cadificacion.
/// </summary>
/// <param name="InXMLFile">Ruta al archivo que se debe de leer</param>
private void LoadProfile(string InXMLFile)
{
using (XmlReader XMLR = new XmlTextReader(InXMLFile))
{
try
{
XMLR.ReadToFollowing("Name");
TSSLblProfile.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("WH");
CboResolucion.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("VBit");
TXTVBit.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Ratio");
CBORatio.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Profile");
CBOProfile.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Nivel");
CboLevel.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("ASample");
CboFreq.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("ABit2");
CBOBitsA.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("Channel");
CBOAChannels.Text = XMLR.ReadElementContentAsString();
XMLR.ReadToFollowing("ExtraParams");
//TXTExtraparams.Text = XMLR.ReadElementContentAsString();
}
catch
{
MessageBox.Show("Parece que el XML no es valido", "VEncoder 2.0.1", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// Funcion para arreglar la duracion de un archivo
/// </summary>
/// <param name="Duration">La duracion en segundos</param>
/// <returns>Regresa un entero con la duracion total del archivo</returns>
/// <remarks>Debido a que mediainfo.dll no es precisa con la duracion; es el metodo facil</remarks>
private int FixTimeDuration(int Duration)
{
if (Duration <= 3600)
{
return Duration + 1;
}else{
return Duration + 60;
}
}
/// <summary>
/// Calcula cuanto tiempo flata para terminar un proceso.
/// </summary>
/// <param name="Porciento">Porcentaje actual completo</param>
/// <param name="TransTime">Tiempo trasncurrido</param>
/// <returns>Regresa lel tiempo restante calculado con los datos proporcionados</returns>
private Single TiempoRestante(Single Porciento, int TransTime)
{
Single Z;
//z = tiempo gastado por cada 1%
if (Porciento == 0)
{
Porciento = 1;
}
Z = TransTime / Porciento;
return (100 - Porciento) * Z;
}
/// <summary>
/// Calcula porcentaje
/// </summary>
/// <param name="ValorActual">Valor actual para hacer el calculo</param>
/// <param name="ValorTotal">Valor total para hacer el calculo</param>
/// <returns>Un numero que representa el valor del porcentaje</returns>
private Single Porcentaje(Single ValorActual, Single ValorTotal)
{
return (ValorActual / ValorTotal) * 100;
}
/// <summary>
/// Obtiene el nombre de archivo desde una ruta completa
/// </summary>
/// <param name="FileName">La ruta de la cual se quiere obtener el nombre de archivo</param>
/// <returns>Un string con el equivalente al nombre de archivo</returns>
/// <remarks>El valor devuelto no incluye la extension</remarks>
private string GetFileName(string FileName)
{
string[] TMPData = FileName.Split((char)92);
return TMPData[TMPData.Length - 1];
}
/// <summary>
/// Obtiene el valor de tiempo desde una linea de resultado de ffmpeg
/// </summary>
/// <param name="FFText">Linea proveniente de FFMPEG</param>
/// <returns>Regresa un numero con el valor en segundos</returns>
private Single GetPositionFromFFline(string FFText)
{
// (\d+)\.\d+x find speed
try
{
if (FFText.StartsWith("FRAME", StringComparison.OrdinalIgnoreCase)) {
Single CurrentValue = 0;
MatchCollection wordColl = Regex.Matches(FFText, @"(\d+):(\d+):(\d+)?(.\d+)");
CurrentValue = (Single)TimeSpan.Parse(wordColl[0].ToString()).TotalSeconds;
return CurrentValue;
}else{
return 1;
}
} catch (Exception) {
return 1;
}
}
private string GetSpeedFromFFLine(string FFText)
{
try
{
if (FFText.StartsWith("FRAME", StringComparison.OrdinalIgnoreCase))
{
string CurrentValue = "0x";
MatchCollection wordColl = Regex.Matches(FFText, @"((\d+)(\.\d+)?)x");
CurrentValue = wordColl[0].ToString();
return CurrentValue;
}
else
{
return "???x";
}
}
catch (Exception)
{
return "!!!x";
}
}
/// <summary>
/// Prepara la linea de comandos y lo necesario para hacer la conversion
/// </summary>
/// <param name="HardWork">Parametro booleano que indica como se debe de hacer el trabajo</param>
/// <remarks>Si HardWork es true, todos lo parametros se pasaran a la conversion; en caso contrario,
/// solo se genera la linea de comandos para usarse de forma externa con FFMPEG</remarks>
private void PrepareWork(bool HardWork = false)
{
List<string> Lote = new List<string>();
List<int> DLote = new List<int>();
if (CHKAll.Checked == true)
{
foreach (ListViewItem LV in lvfilesin.Items)
{
if (LV.Checked)
{
if (CHK2pass.Checked == true)
{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboLevel.Text, CboFPS.Text, CBOCodecA.Text, CBOBitsA.Text, CboFreq.Text,
CBOAChannels.Text, TXTSavePath.Text,TXTExtraParamsVideo.Text,TxtExtraParamsAudio.Text,TxtExtraParamsGen.Text, true, false, false));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboLevel.Text, CboFPS.Text, CBOCodecA.Text, CBOBitsA.Text, CboFreq.Text,
CBOAChannels.Text, TXTSavePath.Text, TXTExtraParamsVideo.Text, TxtExtraParamsAudio.Text, TxtExtraParamsGen.Text, false, true, false));
DLote.Add((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds + 1);
}else{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboLevel.Text, CboFPS.Text, CBOCodecA.Text, CBOBitsA.Text, CboFreq.Text,
CBOAChannels.Text, TXTSavePath.Text, TXTExtraParamsVideo.Text, TxtExtraParamsAudio.Text, TxtExtraParamsGen.Text, false, false, true));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
}
}
}
}else{
foreach (ListViewItem LV in lvfilesin.Items)
{
if (CHK2pass.Checked == true)
{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboLevel.Text, CboFPS.Text, CBOCodecA.Text, CBOBitsA.Text, CboFreq.Text,
CBOAChannels.Text, TXTSavePath.Text, TXTExtraParamsVideo.Text, TxtExtraParamsAudio.Text, TxtExtraParamsGen.Text, true, false, false));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboLevel.Text, CboFPS.Text, CBOCodecA.Text, CBOBitsA.Text, CboFreq.Text,
CBOAChannels.Text, TXTSavePath.Text, TXTExtraParamsVideo.Text, TxtExtraParamsAudio.Text, TxtExtraParamsGen.Text, false, true, false));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
}else{
Lote.Add(BuildCommandLine(LV.Text, CBOCodec.Text, CboResolucion.Text,
TXTVBit.Text, CBORatio.Text, CBOProfile.Text, CboLevel.Text, CboFPS.Text, CBOCodecA.Text, CBOBitsA.Text, CboFreq.Text,
CBOAChannels.Text, TXTSavePath.Text, TXTExtraParamsVideo.Text, TxtExtraParamsAudio.Text, TxtExtraParamsGen.Text, false, false, true));
DLote.Add(FixTimeDuration((int)TimeSpan.Parse(LV.SubItems[3].Text).TotalSeconds));
}
}
}
if (HardWork)
{
TimerElapsed.Enabled = true;
DoWork(Lote, DLote);
}else{
foreach (String LineCom in Lote)
{
RTBCommandLine.Text = RTBCommandLine.Text + LineCom + "\n";
}
}
}
private string BuildExtraCommands(string ExtraParams)
{
return " " + ExtraParams;
}
private string BuildAudioCommand( string AudioCodec, string AudioBitRate = "", string AudioFreq = "", string Channels = "", string ExtraParams = "")
{
if (string.Compare(AudioCodec,"copy",true) == 0){
return " -c:a copy ";
}else{
string TempCommand = " -c:a " + AudioCodec;
if (string.Compare(AudioBitRate, "copy", true) == -1)
TempCommand += " -b:a " + AudioBitRate + "k";
if (string.Compare(AudioFreq, "copy", true) == -1)
TempCommand += " -ar " + AudioFreq ;
if (string.Compare(Channels, "copy", true) == -1)
TempCommand += " -ac " + Channels;
TempCommand += " " + ExtraParams;
return TempCommand;
}
}
private string BuildVideoCommand(string VCodec,string VSize = "", string VBit = "", string VAspect = "",string VProfile = "", string VLevel = "", string VFPS = "",
string ExtraParams = "")
{
if (string.Compare(VCodec, "copy", true) == 0)
{
return " -c:v copy";
}
else
{
string TempCommand = " -c:v " + VCodec;
if (string.Compare(VSize, "copy", true) == -1)
TempCommand += " -s " + VSize;
if (string.Compare(VBit, "copy", true) == -1)
TempCommand += " -b:v " + VBit +"k";
if (string.Compare(VAspect, "copy", true) == -1)
TempCommand += " -aspect " + VAspect;
if (string.Compare(VProfile, "copy", true) == -1)
TempCommand += " -profile:v " + VProfile;
if (string.Compare(VLevel, "copy",true) == -1)
TempCommand += " -level " + VLevel;
if (string.Compare(VFPS, "copy", true) == -1)
TempCommand += " -r " + VFPS;
TempCommand += " " + ExtraParams;
return TempCommand;
}
}
private string BuildCommandLine(string InFile,string VCodec,string VideoSize,string VideoBit,string VAspect,string VProfile, string VLevel,string FrameRate, string ACodec ,string AudioBit,
string AudioFreq, string AChannels,string OutPath , string VidExtraParams, string AudExtraParams, string GenExtraParams,bool ForFirstPass= false,bool AfterPass1=false,bool SinglePass= true)
{
if (ForFirstPass){
return string.Concat("-y -i ", "\"", InFile, "\"", BuildVideoCommand(VCodec, VideoSize, VideoBit, VAspect, VProfile, VLevel, FrameRate, VidExtraParams), " -pass 1 -an ", GenExtraParams, " \"", OutPath, "\\", GetFileName(InFile), ".mp4", "\"");
}
if(AfterPass1){
return string.Concat("-y -i ", "\"", InFile, "\"", BuildVideoCommand(VCodec, VideoSize, VideoBit, VAspect, VProfile, VLevel, FrameRate, VidExtraParams), " -f mp4 -pass 2 ", GenExtraParams ,BuildAudioCommand(ACodec, AudioBit, AudioFreq, AChannels, AudExtraParams), " \"", OutPath, "\\", GetFileName(InFile), ".mp4", "\"");
}
if (SinglePass) {
return string.Concat("-y -i ", "\"", InFile, "\"", BuildVideoCommand(VCodec, VideoSize, VideoBit, VAspect, VProfile, VLevel, FrameRate, VidExtraParams), " -f mp4 ", GenExtraParams , BuildAudioCommand(ACodec, AudioBit, AudioFreq, AChannels, AudExtraParams), " \"", OutPath, "\\", GetFileName(InFile), ".mp4", "\"");
}else{
return "Test";
}
}
/// <summary>
/// Inicia la conversion de los archivos
/// </summary>
/// <param name="Params">Una lista del tipo string que contiene las lineas de comandos a usarse</param>
/// <param name="Durations">Una lista del tipo int que contiene las duraciones de los archivos</param>
private void DoWork(List<string> Params, List<int> Durations)
{
Thread DoConversion = new Thread(new ThreadStart(() => {
int CD = 0;
foreach (string Param in Params)
{
if (RequestForCancel == true)
break;
int TotalPercentConvert = CD * 100;
int CurrentDuration = Durations[CD];
Process Proc = new Process();
StreamReader FFResult;
string CurrentResult;
ProcessStartInfo FFProcess = new ProcessStartInfo("ffmpeg.exe");
FFProcess.Arguments = Param;
FFProcess.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos);
FFProcess.UseShellExecute = false;
FFProcess.WindowStyle = ProcessWindowStyle.Hidden;
FFProcess.RedirectStandardError = true;
FFProcess.RedirectStandardOutput = true;
FFProcess.CreateNoWindow = true;
Proc.StartInfo = FFProcess;
Proc.Start();
PBProgress.BeginInvoke(new Action(() => PBProgress.Maximum = CurrentDuration));
PBGlobalProgress.BeginInvoke(new Action(() => PBGlobalProgress.Maximum = Durations.Count * 100));
FFResult = Proc.StandardError;
do
{
if (RequestForCancel == true)
{
Proc.Kill();
break;
}
CurrentResult = FFResult.ReadLine();
Debug.Print(CurrentResult);
this.BeginInvoke(new Action(() => {
//this.lblProgressInfo.Text = TimeSpan.FromSeconds(GetPositionFromFFline(CurrentResult)).ToString();
this.lblProgressInfo.Text = GetSpeedFromFFLine(CurrentResult);
this.PBProgress.Value = (int)GetPositionFromFFline(CurrentResult);
this.PBGlobalProgress.Value = TotalPercentConvert + (int)Porcentaje(GetPositionFromFFline(CurrentResult),CurrentDuration);
TaskbarProgress.SetValue(this.Handle, this.PBGlobalProgress.Value, this.PBGlobalProgress.Maximum);
}));
}while (Proc.HasExited == false & string.Compare(CurrentResult, "") == 1);
CD = CD + 1;
ElapsedTimeCurrent = 0;
}
IsConvertingSomething = false;
RequestForCancel = false;
ElapsedTimeCurrent = 0;
ElapsedTimeGlobal = 0;
this.BeginInvoke(new Action(() => {
this.lblProgressInfo.Text = "Esperando";
this.PBProgress.Value = 0;
this.PBGlobalProgress.Value = 0;
TaskbarProgress.SetState(this.Handle, TaskbarProgress.TaskbarStates.NoProgress);
}));
}));
DoConversion.Start();
}
/// <summary>
/// Obtiene la informacion de los archivos de entrada
/// </summary>
/// <param name="Files">Archivos de los que se obtendra la informacion.</param>
private void SetInfo(List<string> Files)
{
Thread GetInfo = new Thread(new ThreadStart(() => {
foreach (string Archivo in Files)
{
ListViewItem InfoItem = new ListViewItem(Archivo);
MediaFile mFile = new MediaFile(Archivo);
InfoItem.SubItems.AddRange(new string[] { mFile.Video[0].FrameSize.ToString(),mFile.Video[0].FrameRate.ToString(),
mFile.Video[0].DurationString,mFile.Video[0].Bitrate.ToString(),mFile.Audio.Count.ToString(),
mFile.Audio[0].SamplingRate.ToString(),mFile.Audio[0].Bitrate.ToString(),mFile.Audio[0].Channels.ToString()});
lvfilesin.BeginInvoke(new Action(() => lvfilesin.Items.Add(InfoItem)));
}
this.BeginInvoke(new Action(() => TaskbarProgress.SetState(this.Handle,TaskbarProgress.TaskbarStates.NoProgress)));
}));
GetInfo.Start();
}
#endregion
void FrmMainLoad(object sender, EventArgs e)
{
CBOCodec.SelectedIndex = 0;
CboResolucion.SelectedIndex = 3;
CBORatio.SelectedIndex = 0;
CBOBitsA.SelectedIndex = 4;
CBOProfile.SelectedIndex = 1;
CBOAChannels.SelectedIndex = 1;
CBOCodecA.SelectedIndex = 1;
if (Environment.Is64BitOperatingSystem)
TSLblInfo.Text = "64 Bits";
else
TSLblInfo.Text = "32 Bits";
if (System.IO.File.Exists("ffmpeg.exe") == false)
{
switch (MessageBox.Show("No se puede encontrar FFMPEG.exe\n¿Quieres buscarlo?", "VEncoder 2.0", MessageBoxButtons.YesNo
, MessageBoxIcon.Error))
{
case DialogResult.Yes:
using (OpenFileDialog OpDiag = new OpenFileDialog())
{
OpDiag.Title = "Buscar FFMPEG " + TSLblInfo.Text;
OpDiag.Filter = "FFMpeg|FFmpeg.exe|ffmpeg32|ffmpeg32.exe|ffmpeg64|ffmpeg64.exe|Aplicaciones|*.exe";
DialogResult DR = OpDiag.ShowDialog();
if (DR == DialogResult.OK)
{
System.IO.File.Copy(OpDiag.FileName, "FFMPEG.EXE", true);
}
}
break;
case DialogResult.No:
BtnDo.Enabled = false;
break;
default:
throw new Exception("Invalid value for DialogResult");
}
}
}
private void TSMIopen_Click(object sender, EventArgs e)
{
OpenFileDialog OpDiag = new OpenFileDialog();
OpDiag.Multiselect = true;
OpDiag.Filter = "Todos los archivos.|*.*";
OpDiag.ShowDialog(this);
if (OpDiag.FileNames.Length > 0)
{
List<string> Archivos = new List<string>(OpDiag.FileNames);
TaskbarProgress.SetState(this.Handle, TaskbarProgress.TaskbarStates.Indeterminate);
SetInfo(Archivos);
}
}
void BtnFolderSelClick(object sender, EventArgs e)
{
FolderBrowserDialog FolderSelect = new FolderBrowserDialog();
FolderSelect.ShowNewFolderButton = true;
DialogResult DR = FolderSelect.ShowDialog(this);
if (DR == DialogResult.OK)
{
TXTSavePath.Text = FolderSelect.SelectedPath;
}
}
void BtnDoClick(object sender, EventArgs e)
{
if (lvfilesin.Items.Count < 1)
{
MessageBox.Show("No hay archivos", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
if (TXTSavePath.Text.Length < 3)
{
BtnFolderSelClick(null, null);
return;
}
TaskbarProgress.SetState(this.Handle, TaskbarProgress.TaskbarStates.Normal);
PrepareWork(true);
IsConvertingSomething = true;
BtnDo.Enabled = false;
}
void BtnRemoveClick(object sender, EventArgs e)
{
lvfilesin.Items.Clear();
}
void BtnRemoveCheckClick(object sender, EventArgs e)
{
foreach (ListViewItem LV in lvfilesin.CheckedItems)
{
lvfilesin.Items.Remove(LV);
}
}
void BtnCheckAllClick(object sender, EventArgs e)
{
for (int i = 0; i < lvfilesin.Items.Count; i++)
{
lvfilesin.Items[i].Checked = true;
}
}
void BtnInverSelClick(object sender, EventArgs e)
{
for (int i = 0; i < lvfilesin.Items.Count; i++)
{
lvfilesin.Items[i].Checked =! lvfilesin.Items[i].Checked;
}
}
void FrmMainFormClosing(object sender, FormClosingEventArgs e)
{
if (IsConvertingSomething == true)
{
switch (MessageBox.Show("Hay un trabajo en progreso. ¿Realmente quieres salir?",
"Pregunta", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation)) {
case DialogResult.None:
e.Cancel = true;
break;
case DialogResult.Yes:
break;
case DialogResult.No:
e.Cancel = true;
break;
default:
throw new Exception("Invalid value for DialogResult");
}
}
}
void TimerElapsedTick(object sender, EventArgs e)
{
ElapsedTimeGlobal = ElapsedTimeGlobal + 1;
ElapsedTimeCurrent = ElapsedTimeCurrent + 1;
TSLBLElapsedTime.Text = "Transcurrido: " + TimeSpan.FromSeconds((double)ElapsedTimeGlobal).ToString();
lblPercentProgress.Text = Porcentaje((float)PBProgress.Value, (float)PBProgress.Maximum).ToString() + "%";
TSLBLRemainAc.Text = "Tiempo restante: " + TimeSpan.FromSeconds(
(int)TiempoRestante(Porcentaje((float)PBProgress.Value, (float)PBProgress.Maximum),
ElapsedTimeCurrent)).ToString();
if (IsConvertingSomething == false)
{
TimerElapsed.Enabled = false;
BtnDo.Enabled = true;
TSLBLElapsedTime.Text = "En espera";
TSLBLRemainAc.Text = "En Espera";
}
}
void BtnCancelClick(object sender, EventArgs e)
{
if (IsConvertingSomething == true)
{
switch (MessageBox.Show("Quieres cancelar el trabajo actual",
"Cancelar?",
MessageBoxButtons.YesNo,
MessageBoxIcon.Exclamation))
{
case DialogResult.Yes:
RequestForCancel = true;
break;
case DialogResult.None:
case DialogResult.No:
break;
default:
throw new Exception("Invalid value for DialogResult");
}
}
}
void TSMIMakeCommandClick(object sender, EventArgs e)
{
if (lvfilesin.Items.Count < 1)
{
MessageBox.Show("No hay archivos", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return;
}
if (TXTSavePath.Text.Length < 3)
{
BtnFolderSelClick(null, null);
return;
}
RTBCommandLine.Clear();
PrepareWork(false);
}
void TSMIExitClick(object sender, EventArgs e)
{
Application.Exit();
}
void LnklblLoadProfileLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
}
void TSMISaveProfileClick(object sender, EventArgs e)
{
using (SaveFileDialog SavDiag = new SaveFileDialog())
{
SavDiag.Title = "Guardar perfil";
SavDiag.Filter = "Perfil de VEncoder|*.vepx|Archivo XML|*.xml";
if (SavDiag.ShowDialog() == DialogResult.OK)
SaveProfile(SavDiag.FileName, "Test0005");
}
}
private void TSSLblProfile_Click(object sender, EventArgs e)
{
using (OpenFileDialog OpDiag = new OpenFileDialog())
{
OpDiag.Title = "Cargar perfil";
OpDiag.Filter = "Archivo de perfil VEncoder|*.vepx|XML|*.XML;*.vepx";
if (OpDiag.ShowDialog() == DialogResult.OK)
LoadProfile(OpDiag.FileName);
}
}
}
}
Las funciones que han cambiado son "BuildVideoCommand()" y "BuildAudioCommand()", además se ha agregado la función "BuildExtraCommands(). Las primeras dos funciones ya estaban hechas y solo se actualizaron un poco.
Pero antes, cual es la idea? si el chiste era crear una interfaz para que el usuario no tuviera que escribir nada; y así es, la idea de usar los parámetros extra, es que los usuarios que requieran hacer algo que no está programado en VEncoder, puedan hacerlo y aquellos que requieran algo "más", también puedan hacerlo. Si no se usa ningún parámetro adicional, el programa funcionara cómo venia haciéndolo, pero el hecho de poder "extender" la usabilidad, abre la opción a que la gente experimente. Una vez dicho lo anterior, continuemos con la explicacion.
Las funciones "BuildAudioCommand()" y "BuildVideoCommand()"; ambas recibían parámetros del tipo "string" y su objetivo era construir la linea de comandos. Para hacer uso de los parámetros adicionales, simplemente agregaremos al final de los parámetros que ya teníamos y las funciones devuelven la linea de comandos más los parámetros adicionales si es que se agregan.
La función "BuildExtraCommands()" simplemente toma el texto de una caja de texto y lo agrega al final de la linea de comandos (al igual que las anteriores, si está vacía no pasa nada).
Para agregar los parámetros adicionales, ahora se ha agregado una nueva pestaña llamada "Parámetros Adicionales", y tiene un conjunto de subpestañas en la cual es posible agregar los parámetros.
Tomemos cómo ejemplo las siguiente linea de comandos:
ffmpeg.exe -y -i "X:\VIDEOS\Pokemon Pelicula 20.mkv" -c:v libx264 -s 480x272 -b:v 1200k -aspect 16:9 -level 31 -f mp4 -threads 0 -c:a aac -b:a 128k -ar 44100 -ac 2 "X:\Dropbox\Pokemon Pelicula 20.mkv.mp4"
Ahora, si agregamos en el apartado de Video de los parametros adicionales "-tune animation" y en el apartado general "-t 600 -ss 140", la linea de comados resultante será la siguiente:
ffmpeg.exe -y -i "X:\VIDEOS\Pokemon Pelicula 20.mkv" -c:v libx264 -s 480x272 -b:v 1200k -aspect 16:9 -level 31 -tune animation -f mp4 -threads 0 -t 600 -ss 140 -c:a aac -b:a 128k -ar 44100 -ac 2 "X:\Dropbox\Pokemon Pelicula 20.mkv.mp4"
Cómo podras observar, ahora funcionan otras funciones, si quieres saber cuales están disponibles para el formato mp4 usando el codificador X264, puesde ver la guia de la buena codificacion (en inglés).
A partir de ahora podemos hacer uso extensivo de VEncoder, pero eso trae un par de detalles que aun se deben de resolver, pero el problema es el mismo, cómo aumentar la funcionalidad sin aumentar la complejidad. Cómo de costumbre el código lo puedes descargar de mi dropbox para que lo uses y lo modifiques. Aun queda trabajo pendiente, pero por ahora es todo.
Los leo luego.
1/19/2020 06:59:00 p.m.
csharp
,
ffmpeg
,
programacion
,
Vencoder
Vamos a programar #84 - Camino hacía VEncoder 2.1 - Parametros para FFMpeg.
Hola de nuevo a todos, el día de hoy vamos ver el código actualizado de VEncoder, ahora pasando de la versión 2.00.1 a la versión 2.1; por lo que es una actualización menor pero un poco significativa.
Pero antes que nada, vamos a ver un poco del trasfondo para todo esto. Cómo de costumbre mientras veia los comentarios en twitter, hubo mucha gente que decia que al realizar la conversión de un archivo, muchas veces el resultado no era compatible, o se veia raro o el audio perdía sincronía. Una de las principales razones para crear VEncoder fue mantener las cosas simples. Hace no mucho, bastaba con proporcionar los parámetros que se muestran en la interfaz, de ahi, ffmpeg realizaba la conversión y el resultado era compatible en el 90 de los casos. Pero cómo las cosas avanzan, la complejidad del programa aumento y es capaz de hacer muchas cosas y muy complejas. Cómo es dificil cubrir todas las posibilidades (al menos sin aumentar la complejidad al usuario), primero veremos cómo es que se resuelven algunos de los problemas mencionados usando la linea de comandos y después (en otro post); veremos cómo es que se pueden aplicar las soluciones en VEncoder.
Pero antes que nada, vamos a ver un poco del trasfondo para todo esto. Cómo de costumbre mientras veia los comentarios en twitter, hubo mucha gente que decia que al realizar la conversión de un archivo, muchas veces el resultado no era compatible, o se veia raro o el audio perdía sincronía. Una de las principales razones para crear VEncoder fue mantener las cosas simples. Hace no mucho, bastaba con proporcionar los parámetros que se muestran en la interfaz, de ahi, ffmpeg realizaba la conversión y el resultado era compatible en el 90 de los casos. Pero cómo las cosas avanzan, la complejidad del programa aumento y es capaz de hacer muchas cosas y muy complejas. Cómo es dificil cubrir todas las posibilidades (al menos sin aumentar la complejidad al usuario), primero veremos cómo es que se resuelven algunos de los problemas mencionados usando la linea de comandos y después (en otro post); veremos cómo es que se pueden aplicar las soluciones en VEncoder.
Algunas de las situaciones que me han llegado en los comentarios, es que muchas veces tratan de usar una pista de audio externa, o incluso que dentro del mismo video existen dos o tres pistas de audio (generalmente una por cada idioma) y que al realiza la conversión con la aplicación; por default, siempre tomará la primera de ellas y no necesariamente será la deseada.
Para poder usar otra pista de audio, si la tenemos de forma externa (en otro archivo), podremos usar una línea de comandos cómo la que sigue:
Con ello indicamos que vamos a “ingresar” dos archivos y que la pista de audio; en lugar de usar la que está contenida en el video (si es que la tiene); deberá de usar la que usamos cómo segundo archivo de entrada (por lo general archivos mp3, aac, flac, etc.).
Pero al usar pistas de diferentes fuentes, lo más seguro es que no estén adecuadamente sincronizadas, por lo que será necesario hacer los ajustes para que coincidan los tiempos.
Supongamos que tenemos una situación en donde primero se escucha la voz y después se ve el movimiento de la boca, eso quiere decir que el audio está adelantado. Para solucionarlo, podemos usar un línea de comandos cómo la que sigue:
Con eso haremos que el audio empiece un poco después. Ahora, si se da el caso contrario, basta con usar el tiempo invertido, algo cómo lo que sigue:
Otro de los errores más comunes es que al querer hacer una conversión, el video resultante dice que el perfil que se usa es “High 10@L5.1”. Hace algún tiempo se empezó a emplear el perfil alto de 10bits, y FFMpeg tenia una versión en donde el codificador x264 solamente tenia la capacidad de hacer la conversión en 10 bits (por ahora solo lo diremos de está forma para no entrar en detalles). Y una versión diferente para la conversión de 8 bits.
El problema surge cuando hacemos la conversión de un video que viene codificado usando 10 bits, al intentar hacer la conversión el codificador nos mostrará un error diciendo: “x264 [error]: high profile doesn't support a bit depth of 10
Para solucionarlo, podemos usar una línea de comandos cómo la que sigue:
Con ella ahora la salida de video será de 8Bits en lugar de 10.
Si es el caso contrario y queremos usar la codificación de 10 Bits, podemos usar una línea de comandos cómo la que sigue:
Que pasa si queremos convertir solamente una fracción de nuestro video, en ffmpeg podemos indicar que solamente queremos convertir una parte de un archivo, para poder hacerlo, hay opciones. La primera de ellas es asignar cuanto tiempo es lo que queremos convertir, el parámetro “-t” sirve para indicar cuanto tiempo queremos convertir, y se le puede asignar el tiempo en formato HH:MM:SS.CC o en segundos usando el formato SS.CC, por lo que si queremos convertir un video y que su duración sea de solamente nueve minutos y medio podemos pasar el tiempo cómo: “00:09:30.00” o “570.00”.
Si queremos que la conversión no empiece desde el inicio y en su lugar queremos que empiece en una posición arbitraria, debemos de usar el parámetro “-ss” y al igual que el parámetro anterior, le podemos indicar el tiempo donde queremos que empiece la conversión usando cualquiera de los formatos de tiempo. Al realizar la conversión, se empezará de la posición dada, hasta el final del archivo.
Si queremos que la conversión se haga desde una posición determinada, pero que NO sea hasta el final de nuestro archivo, bastará con usar los dos parámetros anteriores indicando la posición de inicio (con -ss) e indicando cuanto tiempo queremos que dure (con el parámetro -t).
Para lograr una conversión optima, FFmpeg ofrece muchas opciones, pero aquí es donde la cosa se empieza a complicar, ya que al aplicar algunos parámetros, afectaran a otros directamente, entonces siempre es importante probar cuales producen el resultado más óptimo para nuestros archivos.
Y bien, por ahora es todo, estos fueron los problemas mas reportados por los usuarios a mi cuenta de twitter @mpm88g (no es necesario que me sigas para poder escribirme tus dudas). En el siguiente post, veremos cómo implementar todos estos parámetro en VEncoder.
Los leo luego
Para poder usar otra pista de audio, si la tenemos de forma externa (en otro archivo), podremos usar una línea de comandos cómo la que sigue:
ffmpeg.exe -y -i "X:\DOCS\CAMERA\videoplayback.mp4" -i "X:\DOCS\CAMERA\test.m4a" -c:v copy -threads 0 -f mp4 -c:a copy "x:\Videos\tetst.mp4"
Con ello indicamos que vamos a “ingresar” dos archivos y que la pista de audio; en lugar de usar la que está contenida en el video (si es que la tiene); deberá de usar la que usamos cómo segundo archivo de entrada (por lo general archivos mp3, aac, flac, etc.).
Pero al usar pistas de diferentes fuentes, lo más seguro es que no estén adecuadamente sincronizadas, por lo que será necesario hacer los ajustes para que coincidan los tiempos.
Supongamos que tenemos una situación en donde primero se escucha la voz y después se ve el movimiento de la boca, eso quiere decir que el audio está adelantado. Para solucionarlo, podemos usar un línea de comandos cómo la que sigue:
ffmpeg.exe -y -itsoffset -0.50 -i "X:\DOCS\CAMERA\videoplayback.mp4" -i "X:\DOCS\CAMERA\AV.mp4" -c:v copy -threads 0 -f mp4 -c:a copy -map 0:1 -map 1:0 "x:\Videos\TEST.mp4"
Con eso haremos que el audio empiece un poco después. Ahora, si se da el caso contrario, basta con usar el tiempo invertido, algo cómo lo que sigue:
ffmpeg.exe -y -itsoffset 0.50 -i "X:\DOCS\CAMERA\videoplayback.mp4" -i "X:\DOCS\CAMERA\AV.mp4" -c:v copy -threads 0 -f mp4 -c:a copy -map 0:1 -map 1:0 "x:\Videos\TEST.mp4"
Otro de los errores más comunes es que al querer hacer una conversión, el video resultante dice que el perfil que se usa es “High 10@L5.1”. Hace algún tiempo se empezó a emplear el perfil alto de 10bits, y FFMpeg tenia una versión en donde el codificador x264 solamente tenia la capacidad de hacer la conversión en 10 bits (por ahora solo lo diremos de está forma para no entrar en detalles). Y una versión diferente para la conversión de 8 bits.
El problema surge cuando hacemos la conversión de un video que viene codificado usando 10 bits, al intentar hacer la conversión el codificador nos mostrará un error diciendo: “x264 [error]: high profile doesn't support a bit depth of 10
Para solucionarlo, podemos usar una línea de comandos cómo la que sigue:
ffmpeg -y -i "X:\VIDEOS\Pokemon Pelicula 20.mkv" -s 1280x720 -c:v libx264 -b:v 2048k -pix_fmt yuv420p -aspect 16:9 -profile:v high -level 4.1 -crf 10 -threads 0 -c:a copy "X:\\Pokemon Pelicula 20.mkv"
Con ella ahora la salida de video será de 8Bits en lugar de 10.
Si es el caso contrario y queremos usar la codificación de 10 Bits, podemos usar una línea de comandos cómo la que sigue:
ffmpeg -y -i "X:\VIDEOS\Pokemon Pelicula 20.mkv" -s 1280x720 -c:v libx264 -b:v 2048k -pix_fmt yuv420p10le -aspect 16:9 -profile:v high -level 4.1 -crf 10 -threads 0 -c:a copy "X:\\Pokemon Pelicula 20.mkv"
Que pasa si queremos convertir solamente una fracción de nuestro video, en ffmpeg podemos indicar que solamente queremos convertir una parte de un archivo, para poder hacerlo, hay opciones. La primera de ellas es asignar cuanto tiempo es lo que queremos convertir, el parámetro “-t” sirve para indicar cuanto tiempo queremos convertir, y se le puede asignar el tiempo en formato HH:MM:SS.CC o en segundos usando el formato SS.CC, por lo que si queremos convertir un video y que su duración sea de solamente nueve minutos y medio podemos pasar el tiempo cómo: “00:09:30.00” o “570.00”.
Si queremos que la conversión no empiece desde el inicio y en su lugar queremos que empiece en una posición arbitraria, debemos de usar el parámetro “-ss” y al igual que el parámetro anterior, le podemos indicar el tiempo donde queremos que empiece la conversión usando cualquiera de los formatos de tiempo. Al realizar la conversión, se empezará de la posición dada, hasta el final del archivo.
Si queremos que la conversión se haga desde una posición determinada, pero que NO sea hasta el final de nuestro archivo, bastará con usar los dos parámetros anteriores indicando la posición de inicio (con -ss) e indicando cuanto tiempo queremos que dure (con el parámetro -t).
Para lograr una conversión optima, FFmpeg ofrece muchas opciones, pero aquí es donde la cosa se empieza a complicar, ya que al aplicar algunos parámetros, afectaran a otros directamente, entonces siempre es importante probar cuales producen el resultado más óptimo para nuestros archivos.
Y bien, por ahora es todo, estos fueron los problemas mas reportados por los usuarios a mi cuenta de twitter @mpm88g (no es necesario que me sigas para poder escribirme tus dudas). En el siguiente post, veremos cómo implementar todos estos parámetro en VEncoder.
Los leo luego
1/12/2020 01:43:00 p.m.
csharp
,
programacion
,
Vencoder
Bienvenido el 2020.
Hola de nuevo a todos, el día de hoy solo escribo para desearles a todos un 2020 muy productivo. Es cierto que en el 2019 el blog bajo en su producción de contenido, pero eso se debe principalmente a que he tenido algunas actividades que requieren mucho de mi tiempo (y son mentalmente desgastantes) pero aprovechando el nuevo año, trataré de organizarme mejor para garantizar al menos un par de post al mes.
Aun hay mucho por aprender por lo que espero que sigas dándote una vuelta por acá de vez en cuando.
Feliz 2020 y los sigo leyendo luego.
Aun hay mucho por aprender por lo que espero que sigas dándote una vuelta por acá de vez en cuando.
Feliz 2020 y los sigo leyendo luego.
12/31/2019 07:54:00 p.m.
offtopic
Vamos a programar #83 - Expresiones regulares en javascript.
Hola de nuevo a todos, el día de hoy vamos a ver un poco más sobre expresiones regulares, pero ahora usando javscript. Hace poco mientras hacia un programa en javascript, me preguntaron por una forma automática de reemplazar los hashtags de twitter con un vinculo a la dirección correspondiente; es decir; si tenemos #anipoke, que se cree un vinculo a https://twitter.com/hashtag/anipoke (usando #anipoke cómo texto). La respuesta simple, es si, si se puede, pero para poder hacerlo, vamos a emplear expresiones regulares.
El trabajo de hoy es mas o menos entender cómo es que podemos usarlas en Javascript, la mayor parte de las expresiones que usamos en C#; por ejemplo; se pueden usar en javascript así que para probarlas, podemos usar un programa cómo el que sigue.
Por ahora solo vamos a probar algunas expresiones. Al igual que en c#, en el código anterior hay una lista de las 1000 palabras mas comunes y vamos a probar todos los ejemplos que previamente ya habíamos usado.
Hay algunos detalles que hay que "corregir", por ejemplo si buscamos palabras con seis letras veremos que "música" no se resalta, pero eso no quiere decir que no nuestra expresión regular este mal, si no que simplemente está incompleta, y la razón principal es que la letra ú (u con acento) no es considerada una letra "normal" dentro del estándar ascii por lo que simplemente lo considera un carácter NO alfanumérico. La solución para eso es sencilla, pero eso será en el próximo post.
Y bien, por ahora es todo, cómo de costumbre puedes bajar el código de mi dropbox para que lo pruebes y lo edites según lo necesites. En el siguiente post veremos cómo manejar los acentos (y otro tipo de letras) e implementaremos la solución a nuestro problema inicial.
Los leo luego.
El trabajo de hoy es mas o menos entender cómo es que podemos usarlas en Javascript, la mayor parte de las expresiones que usamos en C#; por ejemplo; se pueden usar en javascript así que para probarlas, podemos usar un programa cómo el que sigue.
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<Title>Prueba de expresiones regulares</title>
<head>
<style>
.found
{
color: gray;
background-color: yellow;
}
</style>
<script type="text/javascript">
function FindRegex() {
var Texto = document.getElementById('MAIN').innerHTML;
var Patron = new RegExp(document.getElementById('InputRegex').value,"g");
var Result = Texto.match(Patron);
var Words = [];
var Ret = "";
for (var x = 0; x < Result.length; x++)
{
if (Words.includes(Result[x]) == false) Words.push(Result[x]);
}
for(var i = 0; i < Words.length; i++){
var NewExp = new RegExp("\\b"+Words[i]+"\\b", "g");
Ret = Texto.replace(NewExp, '<span class=\"found\">' + Words[i] + '</span>');
Texto = Ret;
}
document.getElementById('demo').innerHTML = Ret;
}
</script>
</head>
<body>
<div>
<form>
<span>Expresion regular para buscar</span>
<br />
<input id="InputRegex" type="text"></input><br />
<input type="button" onclick="FindRegex()" value="Prueba" /><br />
</form>
</div>
<div id="MAIN">Gente humano persona gente hombre mujer familia amigo conocido colega pareja esposo matrimonio amor Partes del Cuerpo Humano cuerpo pie, pierna talón espinilla
rodilla muslo cabeza cara boca labios diente nariz bigote cabello oreja cerebro estómago brazo codo hombro uña mano muñeca palma dedo trasero, culo, cola abdomen
hígado músculo cuello corazón mente alma+ cintura cadera corazón espalda sangre carne piel hueso pecho resfriado diarrea enfermedad Familia abuela abuelo esposa
esposo hermana hermano hija hijo madre nieta nieto padre prima primo tía tío Vida criatura especie ser vida naturaleza campo bosque selva, jungla desierto costa playa
río laguna, lago mar, océano cerro, monte, montaña Animales animal perro gato vaca cerdo caballo, yegua oveja mono ratón, rata tigre conejo dragón ciervo rana león jirafa
elefante pájaro gallina gorrión cuervo águila halcón pez camarón langosta sardina atún calamar pulpo insecto bicho mariposa polilla saltamontes araña mosca
mosquito cucaracha caracol babosa lombriz marisco molusco lagarto serpiente cocodrilo Plantas plantas pasto / césped flor fruta semilla árbol hoja raíz tallo hongo
ciruela pino bambú nuez almendra castaña Cosechas arroz avena cebada trigo vegetal, verdura patatas, papas judías, guisantes, porotos rábano zanahoria manzana naranja plátano pera castaño
durazno tomate sandía Alimentos alimento comida bebida carne gaseosa Espacio tiempo calendario edad época, era fecha segundo minuto hora día semana entre semana
fin de semana mes año ayer hoy mañana amanecer mediodía tarde anochecer noche lunes martes miércoles jueves viernes sábado domingo ambiente espacio entorno
sol luna estrella clima despejado nublado lluvia nieve viento trueno rayo tifón tormenta cielo Direcciones y Posiciones este oeste sur norte derecha izquierda
arriba, encima abajo, debajo adelante, delante atrás, detrás centro, medio encima alrededor diagonal enfrente cerca lejos adentro, dentro afuera, fuera aquí acá ahí allá allí exterior exterior Materiales
agua caliente calor frío fresco hielo vapor fuego gas aire, atmósfera tierra, suelo metal, metálico hierro cobre oro plata plomo sal barro, lodo arcilla yeso
fumar tabaco opio marihuana Pesos y Medidas peso metro litro gramo kilo centi mili Sociedad sociedad comunidad reunión encuentro calidad estructura administración organización
asociación empresa equipo obligación derecho permiso prohibición autoridad cargo campaña club comisión congreso constitución gobierno democracia dictadura presidente ministro director parlamentario, congresista, senador, diputado
representante capital ciudad pueblo villa estado provincia departamento Economía economía consumo demanda compañía comercio producto transacción almacén hotel fábrica cuenta dinero
billete vuelto, cambio máquina expendedora boleto, billete precio, tarifa Objetos hechos por el ser humano Hogar escritorio silla mesa cama puerta dormitorio habitación cuarto oficina panel puerta ventana entrada hogar
casa apartamento, departamento edificio construcción elevador, ascensor escalera cámara Herramientas aparato cámara aguja clavo hilo cuerda, cordel, cordón bolsillo bolso bolsa paraguas parasol pomo llave
trancar arma escultura cuadro grabado energía electricidad corriente Ropa ropa prenda manga solapa, cuello botón cremallera cinturón zapato gafas Transportes transporte, transporte público, transporte privado tránsito, tráfico
vía férrea tren, ferrocarril subterráneo, metropolitano camino vía ruta calle carretera autopista avenida estación, parada avión aeropuerto automóvil, coche, auto bus, autobús, ómnibus Lenguaje número alfabeto raíz, origen, fuente papel carta
periódico, diario diccionario computadora, ordenador idioma extranjero japonés inglés chino alemán español francés Colores color rojo naranja, anaranjado amarillo verde azul violeta blanco negro rosa, rosado
marrón, café gris Actividades cultura autor arte cine dibujo pintura música religión dios artículo educación escuela instituto colegio universidad clase curso estudio
formación análisis investigación conocimiento idea información dato forma estilo figura elemento comunicación expresión periodismo ciencia aritmética historia geografía educación física deporte carrera
competición, competencia documento informe información noticia necesidad falta significado cáracter personalidad pensamiento memoria recuerdo deseo alegria tristeza enojo placer, éxtasis empatía interés aburrimiento
cansancio sorpresa susto miedo, temor ejemplo ayuda favor apoyo búsqueda duda pregunta respuesta cuestión solicitud decisión elección consejo sugerencia orden control sistema
trabajo empleo profesión esfuerzo Números cero uno dos tres cuatro cinco seis siete ocho nueve diez cien mil millón Espacio cosa
lugar aspecto contenido cantidad área volumen ancho, anchura posición dirección tamaño largo, longitud alto, altura aumento reducción crecimiento fondo frente Sustantivos abstractos esto eso aquello
cual secreto automático formalidad presente pasado futuro acción actividad acto acuerdo actitud capacidad concepto condición caso conjunto grupo creación destrucción origen
destino objetivo, meta función intento logro efecto resultado éxito fracaso causa consecuencia beneficio perjuicio ataque defensa conflicto guerra carácter característica crisis cambio
desarrollo progreso avance retroceso mejora6 deterioro comienzo, inicio transcurso fin, final, cabo etapa fase grado corte interrupción espera diferencia similitud sensación dolor sentido conciencia
percepción fuerza potencia existencia experiencia Adjetivos Adjetivos de calidad bueno, buen malo superior inferior central lateral frontal trasero, posterior cierto falso mayor menor absoluto relativo
caro barato viejo joven nuevo cada cualquier dado actual reciente capaz cuyo, cuya fácil, simple, sencillo difícil, complicado estricto serio general particular común especial raro, extraño
fuerte débil correcto, acertado incorrecto, desacertado contrario, opuesto, inverso diferente, distinto parecido, similar otro diverso Adjetivos de forma alto bajo arriba abajo, debajo adelante, delante atrás, detrás gran grande pequeño amplio angosto
compacto delgado grueso Adjetivos sensoriales ligero pesado suave firme flexible duro blando caluroso frío fresco delicioso, apetitoso horrible dulce picante salado amargo anterior
posterior cercano lejano Sentimiento feliz triste solo solitario contento tranquilo calmo agitado ansioso interesado aburrido cansado sorprenido asustado, atemorizado doloroso picante, ardiente apestoso, maloliente
Verbos Auxiliares ser estar haber Existencia aparecer desaparecer existir cambiar crecer vivir nacer morir romper Movimiento ir venir volver hacer empezar, comenzar, iniciar
terminar, acabar abandonar, dejar hacer partir lanzar sujetar golpear patear golpear señalar apuñalar morder clavar levantar pegar moverse bailar andar saltar caer volar
nadar sentarse correr arrojar lanzar coger agarrar poner alcanzar acercar alejar Sensaciones sentir ver oír, escuchar tocar oler percibir Comunicación comunicarse afirmar
negar decir hablar callar escribir leer Emociones amar querer desear odiar, detestar triste llorar reír enfadado admirar, alabar, elogiar alegrarse, encantado consolar interesarse aburrirse cansarse
sorprenderse asustarse, atemorizarse Actividades analizar pensar dormir cantar morder comer beber acordar afectar generar añadir, agregar mejorar empeorar avanzar retroceder ayudar complicar reunirse
entrevistar abrir, desenvolver jugar recoger levantar tomar vender tener faltar dar recibir romper doblar cortar comprar vender llevar puesto cambiar intercambiar sustituir, reemplazar cerrar
buscar encontrar obtener, conseguir crear creer entrar quedarse salir abandonar dejar comunicar considerar comparar evaluar decidir intentar evitar construir destruir deber poder
conocer entender, comprender atar saber cansarse salir trabajar separar, dividir, partir descansar dormir romper aceptar rechazar descartar acompañar pedir, solicitar pretender proponer sugerir usar hacer
fabricar arreglar, reparar explicar mostrar probar, intentar comprobar, verificar variar esperar necesitar, precisar significar Adverbios Adverbios de cantidad más menos muy mucho poco algo casi aproximadamente exactamente
bastante demasiado sólo tan tanto todo nada cómo cuándo cuánto cuál, cuáles dónde Adverbios de calidad bien mal mejor peor regular despacio deprisa tal
como aprisa adrede claro exacto obvio ante contra inclusive además únicamente especialmente incluso viceversa siquiera finalmente Adverbios temporales antes anteriormente ahora ya
todavía aún mientras durante después luego pronto tarde temprano ayer anoche hoy mañana enseguida ahora de nuevo siempre Adverbios de posibilidad siempre nunca jamás
también tampoco quizá(s) acaso tal vez Otros adverbios aun incluso sólo incluso sobre solamente hasta sin embargo pero por ejemplo sólo, solamente justo apenas a en
desde entre hasta ante demasiado también Pronombres adjetivales este, esta ese, esa aquel, aquella algún, alguna alguien algo ambos Interjecciones sí no Conjunciones si tal vez entonces
cuando, si mientras hacia para de, del con sin así asimismo también aunque y o Locuciones acerca de a lo mejor a pesar de por favor gracias </div><br/>
<div id="demo"></div>
</body>
</html>
Por ahora solo vamos a probar algunas expresiones. Al igual que en c#, en el código anterior hay una lista de las 1000 palabras mas comunes y vamos a probar todos los ejemplos que previamente ya habíamos usado.
\ba\w+\b
\ba\w+n\b
\b(\w+[xyz]|\w+[xyz]\w+|[xyz]\w+)\b
\b\w\w\w\w\w\w\w\w\w\w\w\w\w\w\w\b
\b\w{15}\b
\b\w{10,}\b
Hay algunos detalles que hay que "corregir", por ejemplo si buscamos palabras con seis letras veremos que "música" no se resalta, pero eso no quiere decir que no nuestra expresión regular este mal, si no que simplemente está incompleta, y la razón principal es que la letra ú (u con acento) no es considerada una letra "normal" dentro del estándar ascii por lo que simplemente lo considera un carácter NO alfanumérico. La solución para eso es sencilla, pero eso será en el próximo post.
Y bien, por ahora es todo, cómo de costumbre puedes bajar el código de mi dropbox para que lo pruebes y lo edites según lo necesites. En el siguiente post veremos cómo manejar los acentos (y otro tipo de letras) e implementaremos la solución a nuestro problema inicial.
Los leo luego.
12/08/2019 03:50:00 p.m.
expresiones regulares
,
Javascript
,
programacion
Vamos a programar #82 - Actualizando el velocímetro - Spedometer M V2.
Hola de neuvo a todos, el día de hoy vamos a ver cómo actualizar el velocímetro que previamente ya habíamos hecho a la versión 2.0
Después de varios meses de espera, finalmente he decidido actualizar el velocímetro a la siguiente versión. Ademas de hacer mejoras en la construcción, decidí actualizar el código para hacerlo un poco mas eficiente y funcional.
Lo primero que hay que resaltar es que es conveniente darse una vuelta por el post donde hicimos la primera version, ya que todos los fundamentos están en esa serie de posts.
Para esta versión se agregan solo dos resistencias y tres LED's. Hace unas cuantas semanas vimos cómo medir voltajes usando arduino, para eso usamos un divisor resistivo compuesto por dos resistencias de dos mega ohm y la parte media, la conectamos al pin A5. Cómo el voltaje está dividido a la mitad (puedes ver cómo funciona en el post de de divisores resistivos), en lugar de medir los 4.2v solo mediríamos cómo máximo 2.1v que se resuelve si se multiplica por dos.
Algo que es muy importante resaltar, es que el indicador de la batería no es muy preciso, eso se debe principalmente a que no encontré una hoja de datos del modelo especifico y por las que encontre, si hay diferencia entre marca y marca por lo que hay que ajustar los niveles de acuerdo a lo que se especifique según el tipo de batería que uses.
Y bien, por ahora es todo. En el siguiente post, veremos una explicación detallada del código, por ahora intentare buscar una hoja de datos o trataré de implementar una forma un poco mas precisa de desplegar el porcentaje de la batería. El código cómo de costumbre lo puedes descargar de dropbox (es el mismo link pero actualizado).
Los leo luego.
Después de varios meses de espera, finalmente he decidido actualizar el velocímetro a la siguiente versión. Ademas de hacer mejoras en la construcción, decidí actualizar el código para hacerlo un poco mas eficiente y funcional.
"Mejorando la casa"
La primera construcción la hice en una caja de plástico barato y no era muy estable que digamos, y debido a eso en situaciones de terreno muy empedrado, la débil construcción hacia que el velocímetro fallara. Por eso hice una caja de acrílico (los pasos no lo muestro, ya que es una caja y me quedo horrible) unida con silicon frió y tornillos, gracias a eso las pruebas que he hecho en terrenos realmente accidentados, han sido exitosas. Antes cuando estaba en la caja de plástico, en algunos caminos se reiniciaba (supongo que por la vibración), pero ahora ya no.
Además de manera opcional, incluí un LED de color rojo para saber cuando es que está en operación, no ayuda en nada pero siempre resulta util saber que cuando la luz está encendida, lo demás también debería.
En sustitución del la placa Arduino Uno, decidí usar directamente el micro-controlador ATMEGA328p (en un futuro post veremos cómo hacer un "Minimal arduino").
Las conexiones y el código.
Para las conexiones, bastará con mirar el siguiente diagrama:
Y el siguiente código:
// 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;
const byte Batt100[] = {
B01110,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111
};
const byte Batt75[] = {
B01110,
B10001,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111
};
const byte Batt50[] = {
B01110,
B10001,
B10001,
B10001,
B11111,
B11111,
B11111,
B11111
};
const byte Batt25[] = {
B01110,
B10001,
B10001,
B10001,
B10001,
B10001,
B11111,
B11111
};
const byte Batt0[] = {
B01110,
B10001,
B10001,
B10001,
B10001,
B10001,
B10001,
B11111
};
//Incializar todo
void setup() {
//Serial.begin(9600);
lcd.createChar(0, Batt0);
lcd.createChar(1, Batt25);
lcd.createChar(2, Batt50);
lcd.createChar(3, Batt75);
lcd.createChar(4, Batt100);
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);
}
//Dibujar el icono de bateria***
void DrawBatt(int Value){
lcd.setCursor(15, 1);
if (Value > 90)
lcd.write(byte(4));
if (Value < 91 && Value > 75 )
lcd.write(byte(3));
if (Value < 76 && Value > 50 )
lcd.write(byte(2));
if (Value < 51 && Value > 10 )
lcd.write(byte(1));
if (Value < 11 )
lcd.write(byte(0));
}
// 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 / 1023.0)) * 100;
lcd.setCursor(0, 0);
lcd.print("v=");
lcd.setCursor(2, 0);
lcd.print(Speed);
lcd.setCursor(8, 0);
lcd.print("d=");
lcd.setCursor(10, 0);
lcd.print(Distancia);
lcd.setCursor(0,1);
lcd.print("Rev=");
lcd.setCursor(4,1);
lcd.print(Vuelta);
DrawBatt(map(BattVolt, 300,405,1,100));
digitalWrite(LED1, ledState);
digitalWrite(LED2, ledState);
digitalWrite(LED3, ledState);
if ((millis() - OldTime) > 3000){
ledState = LOW;
Speed = 0.0;
}
}
Lo primero que hay que resaltar es que es conveniente darse una vuelta por el post donde hicimos la primera version, ya que todos los fundamentos están en esa serie de posts.
Para esta versión se agregan solo dos resistencias y tres LED's. Hace unas cuantas semanas vimos cómo medir voltajes usando arduino, para eso usamos un divisor resistivo compuesto por dos resistencias de dos mega ohm y la parte media, la conectamos al pin A5. Cómo el voltaje está dividido a la mitad (puedes ver cómo funciona en el post de de divisores resistivos), en lugar de medir los 4.2v solo mediríamos cómo máximo 2.1v que se resuelve si se multiplica por dos.
Algo que es muy importante resaltar, es que el indicador de la batería no es muy preciso, eso se debe principalmente a que no encontré una hoja de datos del modelo especifico y por las que encontre, si hay diferencia entre marca y marca por lo que hay que ajustar los niveles de acuerdo a lo que se especifique según el tipo de batería que uses.
Y bien, por ahora es todo. En el siguiente post, veremos una explicación detallada del código, por ahora intentare buscar una hoja de datos o trataré de implementar una forma un poco mas precisa de desplegar el porcentaje de la batería. El código cómo de costumbre lo puedes descargar de dropbox (es el mismo link pero actualizado).
Los leo luego.
11/17/2019 04:41:00 p.m.
Arduino
,
programacion
,
velocimetro
Vamos a programar #81 - Midiendo voltajes con arduino
Para poder hacer las pruebas, voy a utilizar un arduino en conjunto a una pantalla LCD, por si quieres adaptar solo la parte que hace la medición, vendrá bien identificado que es lo que hace cada parte.
Para empezar vamos a recordar un par de cosas importantes: Primera. Nunca debemos de exceder los 5v que arduino usa, ingresar un voltaje mayor podría causar daños en tu circuito (en incluso a ti) por lo que hay que extremar las precauciones. Segunda. Cómo sabemos que el voltaje de una batería de iones de litio nunca va a pasar de los 4.2v, podemos hacer los cálculos para que en el divisor resistivo nos de un voltaje similar a este.
Los cálculos.
Primero vamos a empezar con los 14.8v (que serian 4 pilas en serie) y para calcular, tomemos la formula que sirve para calcular el voltaje y sustituyamos valores. Hay que tomar en cuenta que estamos usando el valor nominal por lo que deberiamos de usar resistencia que nos den un valor cercano a 3.7vPara R2 voy a usar una resistencia de 100kΩ, el voltaje que esperamos es menor que 5v pero sabemos que tendremos una mejor medición si usamos múltiplos de 3.7v asi que ese será nuestro objetivo entonces R2 = 100kΩ, V2 = 3.7 y el voltaje de entrada sera 14.8v por lo que V1 = 14.8. Sustituyendo tenemos:
Al realizar los cálculos tenemos que debemos de poner dos resistencias en serie, la primera de ellas de 100kΩ y la segunda de ellas de 300kΩ. Podemos realizar los cálculos con los valores máximos de la pila que serán 4.2v*4 = 16.8 por lo que sustituyendo otra vez tenemos:
Y al realizar las mediciones podemos confirmar que son correctas.
![]() |
| El voltaje de entrada es 16.84v |
![]() |
| Cómo no tengo resistencias de 300kΩ uso tres de 100kΩ, R1 es la resistencia mas cercana a V+ (cable rojo) |
![]() |
| Ahora podemos conectar de manera segura al arduino |
![]() |
| Cómo neuvamente no hay resistencias de 200,003Ω redondeamos al valor decimal mas cercano 200,000Ω |
![]() |
| Ahora tenemos 12.63v |
![]() |
| Igual uso dos resistencias de 100kΩ |
![]() |
| Y nuevamente tenemos un voltaje seguro. |
El código.
El código que sirve para medir el voltaje en arduino es el siguiente:#include <Wire.h>
#include <LiquidCrystal_I2C.h>
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4);
void setup() {
lcd.init();
lcd.backlight();
}
float Volt = 0.0;
int InVolt = 0;
void loop() {
//Conectamos el voltaje de entrada al pin A0 del arduino
//Recordatorio amable: NO DEBE DE EXCEDER LOS 5 Volts
InVolt = analogRead(A0);
Volt = InVolt * (5.0 / 1023.0);
lcd.setCursor(0 , 0);
lcd.print("Voltaje actual");
lcd.setCursor(0 , 1);
lcd.print(Volt);
delay(1000);
}
Cómo podrás observar el código es realmente sencillo, simplemente conectamos el voltaje que queremos medir al pin A0 (v+ y v- a GND), luego cómo arduino cuenta con convertidor analogo-digital de 10 bits, bastara con leer el valor y multiplicarlo por 5 y dividirlo entre 1023 (hay que recordar que 2^10 = 1023) y con esto tendremos un valor que sera el voltaje de la bateria.
Tras realizar varias mediciones, he detectado que el valor que se despliega en el arduino es bastante cercano al de las baterías por lo que ahora podremos revisar un poco el estado de éstas.
Y bien, por ahora es todo, en el siguiente post veremos cómo incorporar el código al velocímetro.
Los leo luego.
Back to basics #7 - Divisores resistivos.
Hola de nuevo a todos, el día de hoy vamos a ver un poco mas sobre el uso de las resistencias.
Hace algún tiempo hicimos un velocímetro para bicicleta, y todo funciona bien salvo un par de detalles. Un de las cosas que luego me pasa, es que la batería se acaba a la mitad del recorrido y cómo me es importante saber cuanta distancia hago en cada uno, siento que si se apaga el velocímetro a mitad del camino, éste simplemente ya no cuenta. Cómo ya van varias veces que me pasa, decidí mostrar la información de la batería en la pantalla (Aprovechando el espacio disponible en ella.) y así al menos tener una idea de cuando debo de cargar la batería.
Antes de empezar, hay que buscar un método para medir el voltaje; hay que recordar que el velocímetro está basado en arduino, esto significa que en las entradas podemos medir niveles de hasta 5V, la batería tiene niveles de 3v a 4.2v, esto significa que en teoría podríamos simplemente conectar la batería a una de las entradas y "medir", pero en general es una mala practica conectar directamente la batería al micro-controlador.
Para poder medir el voltaje de manera segura vamos a usar un divisor de tensión, pero antes de continuar veamos que es:
Y hay una formula que sirve para calcular cual es el voltaje que queremos usar. Para eso miremos la siguiente imagen:
Donde tenemos:
Ahora tomemos cómo ejemplo la siguiente configuración:
Si observas cuidadosamente, tenemos dos resistencia de 4.7 KΩ y 3v a la entrada que si reemplazamos en la formula
tenemos:
Y al realizar las mediciones, podemos comprobar que en efecto los cálculos son correctos:
Hace algún tiempo hicimos un velocímetro para bicicleta, y todo funciona bien salvo un par de detalles. Un de las cosas que luego me pasa, es que la batería se acaba a la mitad del recorrido y cómo me es importante saber cuanta distancia hago en cada uno, siento que si se apaga el velocímetro a mitad del camino, éste simplemente ya no cuenta. Cómo ya van varias veces que me pasa, decidí mostrar la información de la batería en la pantalla (Aprovechando el espacio disponible en ella.) y así al menos tener una idea de cuando debo de cargar la batería.
Antes de empezar, hay que buscar un método para medir el voltaje; hay que recordar que el velocímetro está basado en arduino, esto significa que en las entradas podemos medir niveles de hasta 5V, la batería tiene niveles de 3v a 4.2v, esto significa que en teoría podríamos simplemente conectar la batería a una de las entradas y "medir", pero en general es una mala practica conectar directamente la batería al micro-controlador.
Para poder medir el voltaje de manera segura vamos a usar un divisor de tensión, pero antes de continuar veamos que es:
Un divisor de tensión es una configuración de un circuito eléctrico que reparte la tensión de una fuente entre una o más impedancias conectadas en serie. wikipedia/divisordetensionPara crearlo, simplemente debemos de unir dos resistencias (o mas) en serie y tomar el voltaje de uno de los puntos medios cómo muestra el siguiente diagrama:
Y hay una formula que sirve para calcular cual es el voltaje que queremos usar. Para eso miremos la siguiente imagen:
Donde tenemos:
- V2 = Voltaje de salida
- R2 = El valor de la resistencia 2
- R1 = Valor de la resistencia 1
- V1 = Voltaje de entrada
Ahora tomemos cómo ejemplo la siguiente configuración:
Arreglo de resistencias
![]() |
| Tenemos 3v a la entrada |
Si observas cuidadosamente, tenemos dos resistencia de 4.7 KΩ y 3v a la entrada que si reemplazamos en la formula
tenemos:
Y al realizar las mediciones, podemos comprobar que en efecto los cálculos son correctos:
![]() |
| Medimos "en medio" de las resistencias |
Si no te gusta usar la formula, bastará con usar resistencias del mismo valor y el voltaje será proporcional, en el ejemplo anterior usamos dos resistencias de 4.7 por lo que al dividir entre dos nos resulta 1.5v.
Ahora de manera intuitiva, si usamos tres resistencias de 100kΩ y medimos en la primer sección tendremos una tercera parte del voltaje y si medimos en la segunda, tendremos dos terceras partes, eso significa que si usamos un voltaje de 3v a la entrada, tendríamos voltajes cercanos a 1v y 2v respectivamente. Ahora lo comprobaremos.
![]() |
| Ahora usamos tres resistencias de 100kΩ |
![]() |
| Voltaje en la primer unión |
![]() |
| Voltaje en la segunda unión |
Con esto podemos medir de manera más segura el voltaje directamente con el arduino, pero cómo siempre hay que tener consideraciones. La primera de ellas es que usar este método para reducir el voltaje no es la más conveniente si queremos alimentar un LED' por ejemplo, ya que implicaría un gasto de corriente adicional, pero además no funciona del todo bien y entre mas sea la corriente peor funcionará. La segunda, que valores son los que debemos de usar, eso depende mucho de la aplicación, pero bastara con resistencias dentro del rango de los kΩ y eso se debe a que el propio divisor resistivo gasta cierta potencia, si recordamos la ley del watt, si tenemos un divisor resistivo con dos resistencias de 4.7kΩ y un voltaje de 3v, la potencia que se disipa en las resistencias es de 0.0003w que no es mucho, pero si usamos resistencias de 10Ω en la misma configuración tendríamos un gasto de 0.15w (haciendo técnicamente nada). Para tener una mejor aproximación al resultado que queremos, debemos de usar resistencias de precisión, las resistencias que usé para el segundo ejemplo, no lo son y ahí es cuando se produce la diferencia (los colores dicen que son de 100kΩ pero en realidad son de 98kΩ~99kΩ).
Y bien, por ahora es todo, en el siguiente post veremos cómo medir el voltaje de una bateria usando arduino.
Los leo luego.
10/20/2019 05:55:00 p.m.
aprende
,
Basics
,
Divisores resistivos
,
Resistencias
Vamos a programar #80 - La conjetura de Collatz (Ver. Pascal).
Hola de nuevo a todos, el día de hoy vamos a continuar con re-fritos de viejos programas, pero ahora en lenguajes que no habíamos usado. En esta ocasión vamos continuar con la prueba de la conjetura de Collatz pero en el lenguaje de programación pascal.
Antes de continuar, es importante mencionar la importancia de saber crear un programa en un lenguaje distinto al que estamos acostumbrados usando los fundamentos del lenguaje, en este caso, deberías de ser capas de crear este programa leyendo los post de aprendizaje que hice para pascal (Para el resto de lenguajes no hay pero recuerda que mi objetivo no es enseñarte a programar). Y bien ahora continuemos con el código.
El código en Pascal que hace funcionar las cosas es el siguiente:
Cómo podrás ver, el programa consta de dos funciones, la primera de ellas en orden de aparición es "IsEven()" y recibe un parámetro del tipo "integer" y sirve para determinar si el número que se paso cómo argumento es par o no; si el valor es par, la funcion devuleve "true", en caso contrario devuelve "false".
La siguiente función es "CalculateCollatz" aquí es donde se hace el calculo (siguiendo las reglas de la conjetura). Si revisaste alguno de los post de aprendizaje para Pascal, pero mas específicamente el post "Learning Machine #5", verás que hacemos uso de "if" y hace poco tiempo alguien me pregunto: "¿Que pasa si tengo más de una línea de código después de mi if?". Y pido una disculpa ya que en su momento lo olvide por completo pero si cambia un poco el uso de "if" si es de una línea o no. Cuando usamos la instrucción "if" si es de múltiples lineas, se deberá de hacer uso de la palabra reservada "begin" seguida de todo el código que se requiera ejecutar en la condición y se termina con "end;" si se usan instrucciones "else" (de igual manera que con if), cada una deberá de ir con su respectivo inicio y final, pero solo la última deberá tener punto y coma.
Y bien, por ahora es todo, cómo de costumbre puedes bajar el código de mi dropbox para que lo pruebes, pero además, puedes probarlo en linea por si no quieres instalar el compilador. En el siguiente post continuaremos con mas programación y con mas refritos hasta que se me ocurra algo bueno.
Los leo luego.
Antes de continuar, es importante mencionar la importancia de saber crear un programa en un lenguaje distinto al que estamos acostumbrados usando los fundamentos del lenguaje, en este caso, deberías de ser capas de crear este programa leyendo los post de aprendizaje que hice para pascal (Para el resto de lenguajes no hay pero recuerda que mi objetivo no es enseñarte a programar). Y bien ahora continuemos con el código.
El código en Pascal que hace funcionar las cosas es el siguiente:
program Collatz (output);
uses crt, sysutils;
var MyVal: integer;
function IsEven (Number:integer): boolean;
begin
if Number mod 2 = 1 then
exit (false)
else
exit (true);
end;
procedure CalculateCollatz (Number:integer);
begin
while Number > 1 do
begin
if IsEven (Number) = false then
begin
Number:= Number * 3 + 1;
writeln (Number);
end
else
begin
Number:= Number div 2;
writeln (Number);
end;
end;
end;
begin
writeln('Inserta un numero');
readln(MyVal);
CalculateCollatz(MyVal);
readln();
end.
Cómo podrás ver, el programa consta de dos funciones, la primera de ellas en orden de aparición es "IsEven()" y recibe un parámetro del tipo "integer" y sirve para determinar si el número que se paso cómo argumento es par o no; si el valor es par, la funcion devuleve "true", en caso contrario devuelve "false".
La siguiente función es "CalculateCollatz" aquí es donde se hace el calculo (siguiendo las reglas de la conjetura). Si revisaste alguno de los post de aprendizaje para Pascal, pero mas específicamente el post "Learning Machine #5", verás que hacemos uso de "if" y hace poco tiempo alguien me pregunto: "¿Que pasa si tengo más de una línea de código después de mi if?". Y pido una disculpa ya que en su momento lo olvide por completo pero si cambia un poco el uso de "if" si es de una línea o no. Cuando usamos la instrucción "if" si es de múltiples lineas, se deberá de hacer uso de la palabra reservada "begin" seguida de todo el código que se requiera ejecutar en la condición y se termina con "end;" si se usan instrucciones "else" (de igual manera que con if), cada una deberá de ir con su respectivo inicio y final, pero solo la última deberá tener punto y coma.
Y bien, por ahora es todo, cómo de costumbre puedes bajar el código de mi dropbox para que lo pruebes, pero además, puedes probarlo en linea por si no quieres instalar el compilador. En el siguiente post continuaremos con mas programación y con mas refritos hasta que se me ocurra algo bueno.
Los leo luego.
10/13/2019 04:57:00 p.m.
aprende
,
conjetura de collatz
,
pascal
,
programacion
Vamos a programar #79 - La conjetura de Collatz (Ver. Python).
Hola de nuevo a todos, el día de hoy vamos a ver un poco más de Python.
En el último post vimos un acercamiento a Python y si bien el blog nunca se ha tratado de "tutoriales" para aprender a programar, mucha gente pidió que adaptara un programa de los que previamente ya habíamos hecho en blog. Ya que solo será la versión adaptada a Python de la conjetura de Collatz, te recomiendo echarle un vistazo a los post que hicimos tanto para Android (Java) cómo para Windows (C#).
Ahora veamos el código que hace funcionar las cosas.
Cómo verás, el programar consta de solo dos funciones; la primera de ellas en orden de aparición es: "IsEven" que recibe un número parámetro y su función es determinar si un número es par o no. Si al hacer la operación "mod" el resultado es uno, eso significa que el número no es par y la funcion devuelve "false" en caso contrario, la función devuelve "true" (que indica que el número es par, valga la redundancia).
La siguiente función es "CalculateCollatz" y tal cómo su nombre lo indica, aqui es en donde se realizan los cálculos y se muestran en pantalla (siguiendo las reglas de la conjetura).
Finalmente solo mandamos a llamar a la función "CalculateCollatz()" que recibirá cómo parámetro un numero que el usuario ingresará.
Y bien, cómo puedes ver, es relativamente fácil portar los programa que ya tenioamos a Python, hay que tomar una cuantas consideraciones en cuanto al lenguaje (en comparación a los otros), pero python resulta realmente poderoso a la hora de escribir un programa. Cómo de costumbre puedes bajar el programa de mi dropbox para que lo pruebes o bien, puedes copiarlo para probarlo en el editor en linea
En el último post vimos un acercamiento a Python y si bien el blog nunca se ha tratado de "tutoriales" para aprender a programar, mucha gente pidió que adaptara un programa de los que previamente ya habíamos hecho en blog. Ya que solo será la versión adaptada a Python de la conjetura de Collatz, te recomiendo echarle un vistazo a los post que hicimos tanto para Android (Java) cómo para Windows (C#).
Ahora veamos el código que hace funcionar las cosas.
#Prueba de la conjetura de collatz en Python
def IsEven(Number):
if (Number % 2 == 1):
return False
else:
return True
def CalculateCollatz(Number):
while Number > 1:
if (IsEven(Number) == False):
Number = Number * 3 + 1
print(Number)
else:
Number = Number / 2
print(Number)
CalculateCollatz(int(input('Ingresa un numero\n')))
input()
Cómo verás, el programar consta de solo dos funciones; la primera de ellas en orden de aparición es: "IsEven" que recibe un número parámetro y su función es determinar si un número es par o no. Si al hacer la operación "mod" el resultado es uno, eso significa que el número no es par y la funcion devuelve "false" en caso contrario, la función devuelve "true" (que indica que el número es par, valga la redundancia).
La siguiente función es "CalculateCollatz" y tal cómo su nombre lo indica, aqui es en donde se realizan los cálculos y se muestran en pantalla (siguiendo las reglas de la conjetura).
Finalmente solo mandamos a llamar a la función "CalculateCollatz()" que recibirá cómo parámetro un numero que el usuario ingresará.
Y bien, cómo puedes ver, es relativamente fácil portar los programa que ya tenioamos a Python, hay que tomar una cuantas consideraciones en cuanto al lenguaje (en comparación a los otros), pero python resulta realmente poderoso a la hora de escribir un programa. Cómo de costumbre puedes bajar el programa de mi dropbox para que lo pruebes o bien, puedes copiarlo para probarlo en el editor en linea
10/06/2019 01:30:00 p.m.
conjetura de collatz
,
programacion
,
python
Vamos a platicar #10 - Hoy hay que celebrar.
Hola de nuevo a todos, el día de hoy solo escribo para platicar sobre un par de acontecimientos de reciente suceso.
Cómo muchos sabrán, soy un seguidor de la serie Pokémon; incluso he escrito en varias ocasiones cosas un tanto relacionadas.
Pero hoy es un día relativamente importante para todos los seguidores de la serie, ya que casi después de 20 años la serie ha dado un paso importante para lo que podría ser su culminación. Antes, un recordatorio amable; si no eres seguidor de la serie, eres libre de ir y leer otro de los artículos, ya que este post solo hablará de lo acontecido con la serie. Bien, retomando lo anterior. Ha pasado tanto tiempo que muchos creímos que Ash nunca ganaría un campeonato, pero si una lección hemos aprendido es que el que persevera alcanza.
Los leo luego.
![]() |
| La imagen no tiene nada que ver con el contenido del post. |
Pero hoy es un día relativamente importante para todos los seguidores de la serie, ya que casi después de 20 años la serie ha dado un paso importante para lo que podría ser su culminación. Antes, un recordatorio amable; si no eres seguidor de la serie, eres libre de ir y leer otro de los artículos, ya que este post solo hablará de lo acontecido con la serie. Bien, retomando lo anterior. Ha pasado tanto tiempo que muchos creímos que Ash nunca ganaría un campeonato, pero si una lección hemos aprendido es que el que persevera alcanza.
— Hugo G (@Mpm88G) September 15, 2019Y bien, por ahora es todo. si notaste la poca actividad del blog, en parte se debía a que estaba a la expectativa de este resultado (en adición a otros deberes), pero en octubre reanudare las publicaciones con nuevos proyectos, para cerrar el año cómo es debido.
Los leo luego.
Suscribirse a:
Entradas
(
Atom
)



































