miércoles, 27 de enero de 2010

Informes Crystal Reports

En esta ocasión veremos la manera de mostrar un reporte sencillo. La apariencia final de nuestra aplicación será la siguiente:

 

Al presionar el botón Imprimir, se abrirá la siguiente pantalla:


Nuestro proyecto de llama proReportes, y deberá quedar organizado de la siguiente manera:


Utilizamos una base de datos llamada dbPrac.mdb, la cual contiene una tabla llamada CAT_CLIENTES. En el archivo adjunto viene tal base de datos.
Por adelantado te digo que esta conformada (la tabla) de la siguiente manera:



Campo
Tipo
Descripción
ID_CLIENTE
TEXTO
Campo llave
PATERNO
Texto
Apellido paterno
MATERNO
Texto
Apellido materno
NOMBRE
Texto
Nombre
RFC
Texto
RFC
DIRECCION
Texto
Dirección
TELEFONO
Texto
Telefono
LIM_CRED
Doble
Límite de
crédito

Al formulario frmCatClientes, le agregamos 8 TextBox y 8 Label, con las siguientes características:


Objeto(nombre)
Tipo
Propiedad
Valor
txtID_CLIENTE
TextBox
Text
(vacío)
txtPATERNO
TextBox
Text
(vacío)
txtMATERNO
TextBox
Text
(vacío)
txtNOMBRE
TextBox
Text
(vacío)
txtRFC
TextBox
Text
(vacío)
txtDIRECCION
TextBox
Text
(vacío)
txtTELEFONO
TextBox
Text
(vacío)
txtLIM_CRED
TextBox
Text
(vacío)
lblID_CLIENTE
Label
Text
Clave
lblPATERNO
Label
Text
Paterno:
lblMATERNO
Label
Text
Materno:
lblNOMBRE
Label
Text
Nombre:
lblRFC
Label
Text
RFC
lblDIRECCION
Label
Text
Dirección:
lblTELEFONO
Label
Text
Teléfono:
lblLIM_CRED
Label
TExt
Límite de crédito
Images
ImageList
barBotones
ToolBar

Agrega imágenes al ImageList y botones al ToolBar (que se parezcan a las arriba mostradas). En el archivo adjunto incluiré las imágenes (en realidad son iconos) que utilice para este ejemplo. 
Agrega también un MainMenu como se muestra:


Al formulario frmReportes, agrega un CrystalReportViewer y ponle como nombre crvReportes, y el la propiedad Dock del Viewer selecciona Fill. El formulario debe quedar como se muestra:


Ahora agregaremos un DataSet, este nos servirá para enlazar nuestro reporte con los datos. Pon mucha atención a lo que a continuación se muestra:
1. Agrega un nuevo
elemento(DataSet
)
:



Aparece algo como lo siguiente:


2.
Selecciona Explorador de Servidores.



3. Con el botón derecho del mouse, sobre Conexiones de datos, selecciona Agregar conexión...


4.
Conéctate a la base de datos de Access:




5. Después de lo anterior, aparece (más o menos) así el Explorador de servidores:


6. Expande la conexión (tal como se muestra en la figura) y arrastra la tabla
CAT_CLIENTES como se muestra:



7. Cierra el Explorador de servidores, graba los cambios y ¡listo!, terminamos el DataSet.
Ahora crearemos nuestro reporte:
1.
Agrega un nuevo elemento al proyecto (CrystalReports
). Pon el nombre rptCatClientes y presiona Abrir:



2. Selecciona Mediante el asistente de informes y presiona Aceptar:


3. Expande como se muestra a continuación:


4. Selecciona la tabla CAT_CLIENTES y presiona el botón Insertar tabla. Presiona Siguiente.


5. Agrega todos los campos (con el botón Agregar). Ve a la casilla Estilo, escribe el título del reporte (Catalogo de Clientes) y presiona Finalizar.
Ahora vamos a programar
...


Código:
Primero el código de
clsMain:


Public Class clsMain
    ''Esta es la cadena de conexión
    ''Es necesaria para obtener conectividad con la base de datos
    Public CnnStr As String = _
            "Provider=Microsoft.Jet.OLEDB.4.0;" & _
            "Data Source=..\Data\dbPrac.mdb;" & _
            "Persist Security Info=False "
End Class
 
Después del código del formulario (frmCatClientes):

''Programador: JUAN GABRIEL CASTILLO TURRUBIATES(poner tu nombre)
'' las instrucciones Imports, van antes de cualquier otra instrucción
Imports System.Data.OleDb
Imports CrystalDecisions.CrystalReports.Engine
Public Class frmCatClientes
    Inherits System.Windows.Forms.Form
    ''una instancia a la clase clsMain
    Dim miClsMain As New clsMain()
    ''Declaramos una conexión
    Dim cnnCatClientes As New OleDbConnection(miClsMain.CnnStr)
    ''Declaramos un Comando
    Dim cmdCatClientes As New OleDbCommand("SELECT * " & _
                            "FROM CAT_CLIENTES", cnnCatClientes)
    ''Declaramos un Data Adapter
    Dim daCatClientes As New OleDbDataAdapter(cmdCatClientes)
    ''Un DataSet
    Dim dsCatClientes As New DataSet()
    ''''y por último, el importantísimo Command Builder
    Dim cbCatClientes As New OleDbCommandBuilder(daCatClientes)
    Private Sub frmCatClientes_Load(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles MyBase.Load
        Inicializar()
        HabilitaBotones(True)
        HabilitaCaptura(False)
    End Sub
    Private Sub Imprimir()
        Try
            'Código para mostrar el reporte
            'Lo primero que hacemos es declarar una instancia
            'del data set dsRepCatClientes
            Dim mi_dsRepCatclientes As New dsRepCatClientes()
            'Lo llenamos con el contenido de la tabla CAT_CLIENTES
            daCatClientes.Fill(mi_dsRepCatclientes, "CAT_CLIENTES")
            'Declaramos una instancia del Reporte
            Dim mi_rptCatClientes As New rptCatClientes()
            'Le indicamos al reporte que tome los datos
            'del DataSet
            mi_rptCatClientes.SetDataSource(mi_dsRepCatclientes)
            'Delcaramos una instancia del formulario frmReprotes
            Dim miForma As New frmReportes()
            'Le indicamos que debe mostrar mi_rptCatClientes
            miForma.crvReportes.ReportSource = mi_rptCatClientes
            'que muestre el titulo "Reporte de Clientes"
            miForma.Text = "Reporte de Clientes"
            'Mostramos el formulario (el cual contiene el reporte)
            miForma.Show()
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Imprimir", _
            MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    Private Function BuscarRegistro(ByVal prmPos As Integer) As Integer
        'Código para buscar un registro
        'Recibe como parámetro la posición actual en el DataSet
        'para, en el caso de no encontrar el registro solicitado,
        'regresar la posición del registro antes de iniciar.
        'Lo que hace este procedimiento es
        'regresar la posición en la que se encuentra el registro
        'solicitado, en caso de no encontrarlo, regresa la posición
        'antes de iniciar la búsqueda
        Try
            Dim dvCatClientes As DataView = _
                    New DataView(dsCatClientes.Tables(0), "", _
                    "ID_CLIENTE", DataViewRowState.CurrentRows)
            'Declaramos varID_CLIENTE
            Dim varID_CLIENTE As String = ""
            'Preguntamos la clave del cliente que se desea buscar
            varID_CLIENTE = InputBox("Introduce la clave a buscar", "Buscar")
            If Not varID_CLIENTE = "" Then
                'Regresamos el index del cliente encontrado
                Return dvCatClientes.Find(varID_CLIENTE)
                Exit Function
            Else
                'Si no se especificó el cliente, regresamos la
                'posición (index) del cliente original(prmPos)
                MessageBox.Show("La búsqueda no se puede realizar", _
                "Información del sistema", MessageBoxButtons.OK, _
                MessageBoxIcon.Information)
                Return prmPos
                Exit Function
            End If
        Catch ex As Exception
            'En caso de error, suponiendo que no se encontró el cliente,
            'regresamos la posición original del cliente(prmPos)
            MessageBox.Show(ex.Message, "Error", _
            MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return prmPos
            Exit Function
        End Try
    End Function
    Private Sub Actualizar()
        Try
            dsCatClientes.Clear() 'Limpiar el DataSet
            daCatClientes.Fill(dsCatClientes, "CAT_CLIENTES")
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Información del sistema", _
            MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    Private Sub Inicializar()
        Try
            With cnnCatClientes
                ''Verificamos el estado de la conexión
                If .State = 1 Then 'si esta abierta
                    .Close()    'cerramos
                End If
                .Open() '' abrimos la conexión
            End With
            ''Cargamos el DataSet Con los datos de La tabla
            daCatClientes.Fill(dsCatClientes, "CAT_CLIENTES")
            ''Enlazamos los Objetos
            txtID_CLIENTE.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.ID_CLIENTE")
            txtPATERNO.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.PATERNO")
            txtMATERNO.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.MATERNO")
            txtNOMBRE.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.NOMBRE")
            txtRFC.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.RFC")
            txtDIRECCION.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.DIRECCION")
            txtTELEFONO.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.TELEFONO")
            txtLIM_CRED.DataBindings.Add("Text", dsCatClientes, _
                "CAT_CLIENTES.LIM_CRED")
        Catch ex As Exception
            ''Esto ocurriría solo en el caso de que ocurra un error
            MessageBox.Show(ex.Message, "Info del Sistema", _
            MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    Private Sub Grabar()
        Try
            'Indicamos que termine la edición actual
            Me.BindingContext(dsCatClientes, _
            "CAT_CLIENTES").EndCurrentEdit()
            'Actializamos la Base de datos
            daCatClientes.Update(dsCatClientes, "CAT_CLIENTES")
            HabilitaBotones(True)
            HabilitaCaptura(False)
        Catch ex As Exception
            MsgBox(ex.Source & "; " & ex.Message, _
            MsgBoxStyle.OKOnly, "Ocurrió un error")
        End Try
    End Sub
    Private Sub Nuevo()
        Try
            'Indicamos que termine la edición actual
            Me.BindingContext(dsCatClientes, _
            "CAT_CLIENTES").EndCurrentEdit()
            'Preparamos al DataSet para aceptar un Registro nuevo
            Me.BindingContext(dsCatClientes, "CAT_CLIENTES").AddNew()
            HabilitaBotones(False)
            HabilitaCaptura(True)
        Catch ex As Exception
            MsgBox(ex.Source & "; " & ex.Message)
        End Try
    End Sub
    Private Sub Eliminar()
        ''Este procedimiento es el encargado de eliminar un registro.
        ''Lo que hacemos es obtener la posición en la que nos encontramos
        ''Y después eliminamos el registro que se encuentra en esa posición
        ''Otra manera de eliminar seria ejecutar una instrucción SQL que
        ''elimine el registro (de acuerdo con la ID_CLIENTE)
        ''y después ejecutar el procedimiento Inicializar.
        Dim Resp As String
        Try
            Resp = MsgBox("¿Esta seguro de eliminar el registro? " & _
            vbCrLf & "Nota: Eliminar registros puede perjudicar " & _
            "la ejecución.", MsgBoxStyle.OKCancel, "Eliminar Registro")
            If Resp = vbOK Then
                Dim Registro As Integer
                Registro = Me.BindingContext(dsCatClientes, _
                    "CAT_CLIENTES").Position
                Me.BindingContext(dsCatClientes, _
                    "CAT_CLIENTES").EndCurrentEdit()
                Me.BindingContext(dsCatClientes, _
                    "CAT_CLIENTES").RemoveAt(Registro)
                daCatClientes.Update(dsCatClientes, "CAT_CLIENTES")
                Me.BindingContext(dsCatClientes, _
                    "CAT_CLIENTES").Position = 0
            End If
        Catch ex As Exception
            MsgBox(ex.Source & "; " & ex.Message, _
            MsgBoxStyle.OKOnly, "Ocurrió un error")
        End Try
    End Sub
    Private Sub Cancelar()
        Try
            'Cancelamos
            Me.BindingContext(dsCatClientes, _
                "CAT_CLIENTES").CancelCurrentEdit()
            HabilitaBotones(True)
            HabilitaCaptura(False)
        Catch ex As Exception
            MsgBox(ex.Source & "; " & ex.Message, _
            MsgBoxStyle.OKOnly, "Ocurrió un error")
        End Try
    End Sub
    Private Sub HabilitaBotones(ByVal Habilitar As Boolean)
        btnInicio.Enabled = Habilitar
        btnAnterior.Enabled = Habilitar
        btnSiguiente.Enabled = Habilitar
        btnFinal.Enabled = Habilitar
        btnNuevo.Enabled = Habilitar
        mnuNuevo.Enabled = Habilitar
        btnModificar.Enabled = Habilitar
        mnuModificar.Enabled = Habilitar
        btnEliminar.Enabled = Habilitar
        mnuEliminar.Enabled = Habilitar
        btnBuscar.Enabled = Habilitar
        mnuBuscar.Enabled = Habilitar
        btnActualizar.Enabled = Habilitar
        btnImprimir.Enabled = Habilitar
        btnGrabar.Enabled = Not Habilitar
        mnuGrabar.Enabled = Not Habilitar
        btnCancelar.Enabled = Not Habilitar
        mnuCancelar.Enabled = Not Habilitar
        btnSalir.Enabled = Habilitar
        mnuSalir.Enabled = Habilitar
    End Sub
    Private Sub HabilitaCaptura(ByVal Habilitar As Boolean)
        txtID_CLIENTE.Enabled = Habilitar
        txtPATERNO.Enabled = Habilitar
        txtMATERNO.Enabled = Habilitar
        txtNOMBRE.Enabled = Habilitar
        txtRFC.Enabled = Habilitar
        txtDIRECCION.Enabled = Habilitar
        txtTELEFONO.Enabled = Habilitar
        txtLIM_CRED.Enabled = Habilitar
    End Sub
    Private Sub barBotones_ButtonClick(ByVal sender As System.Object, _
    ByVal e As System.Windows.Forms.ToolBarButtonClickEventArgs) _
    Handles barBotones.ButtonClick
        Select Case barBotones.Buttons.IndexOf(e.Button)
            Case Is = 0
                Try
                    Me.BindingContext(dsCatClientes, "CAT_CLIENTES").Position = 0
                Catch ex As Exception
                    MsgBox(ex.Source & "; " & ex.Message)
                End Try
            Case Is = 1
                Try
                    Me.BindingContext(dsCatClientes, "CAT_CLIENTES").Position -= 1
                Catch ex As Exception
                    MsgBox(EX.Source & "; " & EX.Message)
                End Try
            Case Is = 2
                Try
                    Me.BindingContext(dsCatClientes, "CAT_CLIENTES").Position += 1
                Catch ex As Exception
                    MsgBox(EX.Source & "; " & EX.Message)
                End Try
            Case Is = 3
                Try
                    Me.BindingContext(dsCatClientes, "CAT_CLIENTES").Position = _
                    Me.BindingContext(dsCatClientes, "CAT_CLIENTES").Count - 1
                Catch ex As Exception
                    MsgBox(EX.Source & "; " & EX.Message)
                End Try
            Case Is = 6
                Nuevo()
            Case Is = 7
                'Modificar
                HabilitaBotones(False)
                HabilitaCaptura(True)
            Case Is = 8
                Eliminar()
            Case Is = 9
                'Buscar registro
                Me.BindingContext(dsCatClientes, "CAT_CLIENTES").Position = _
                BuscarRegistro(Me.BindingContext(dsCatClientes, "CAT_CLIENTES").Position)
            Case Is = 10
                Actualizar()
            Case Is = 11
                Imprimir()
            Case Is = 12
                Grabar()
            Case Is = 13
                Cancelar()
            Case Is = 14
                'Salir(cerrar el formulario)
                Me.Close()
        End Select
    End Sub
End Class

Convertir un archivo WAV a MP3

La idea en este tutorial es como su titulo lo indica, convertir un archivo que esta en formato WAV, en un archivo con formato MP3. Una de las mejores soluciones para lograr este objetivo es usar la DLL de LAME.

¿Que es LAME?
LAME es un proyecto, que tiene como finalidad crear un compresor/descompresor de mp3/wav de gran calidad, que por cierto ya lo ha logrado, pero igualmente se le sigue agregando opciones.
LAME es un programa de libre distribución que es mantenido por una grana cantidad de personas.
LAME se lo puede encontrar como EXE (lame.exe) o como DLL (lame_enc.dll). En el caso del EXE podemos convertir archivos de un formato a otro, desde la linea de comandos, en cambio en la version DLL, la idea es que podemos hacer llamadas a esta desde nuestra aplicacion escrita en cualquier lenguaje, y asi crear una aplicacion totalmente grafica que utiliza LAME.
 
Todo el codigo fuente de LAME esta disponible para el que lo quiera bajar. El mismo se encuentra en lenguaje C, tambien podemos obtener información sobre que funciones tiene la dll, que estructuras usar, etc.. Pero si bien se dispone de esta informacion, cuando debemos "traducir" la estructuras desde "C" a otro lenguaje como VB .NET, C# o cualquier otro, puede provocar varios dolores de cabezas si no tenemos realmente claro que espera recibir la DLL, y que le estamos enviando. La página desde la cual se puede bajar el codigo, y obtener mas información es: http://www.lame.sourceforge.net/

Acerca de la implementación
Para llevar a cabo esta idea, vamos a crear una biblioteca de clases en C#, en la cual tendremos las funciones que necesitamos para trabajar desde nuestra aplicación. Aclaro que sólo implemente lo justo y necesario para esta aplicacion en particular, y de hecho hay una gran cantidad de funciones que expone lame_enc.dll que ni siquiera declare.
Por otro lado, vamos a crear una aplicación en VB .NET que utilice la biblioteca creada en C#, para asi lograr tener una aplicacion que convierta un archivo Wav en MP3.

A Trabajar
Vamos a crear varias clases, las cuales van a pertenecer al espacio de nombres "Sonido". Dentro del namespace "Sonido.MP3.LAME" tendremos todo lo referido a la comunicacion con la DLL de LAME, y en el namespace "Sonido.MP3" tendremos una clase llamada ConversorMP3 que es la que nos permitira convertir el archivo WAV en MP3, sin necesidad de conocer los detalles de LAME (Estructuras, funciones, tipos de datos, etc) del lado de nuestra aplicación.
Para no llenar la pagina de código, aqui sólo voy a mostrar como utilizar la biblioteca de clases desde la aplicación que queremos crear. En fin, basta de hablar y vamos a lo nuestro: El codigo.




Imports System.Runtime.InteropServices
Imports Sonido.MP3

Public
Class Form1

       
Inherits

System.Windows.Forms.Form


#
Region "
Código generado por el Diseñador de Windows Forms "


....
#End Region
        'Cargo los Bitrates posibles para los archivos MP3 que creemos


        Dim
ValoresCalidadPersonalizada()
As
LAME.BitRates =
{LAME.BitRates._8, _


                                                             
LAME.BitRates._16, _

                                                             LAME.BitRates._24,
_                                                              LAME.BitRates._32,
_

                                                           
  LAME.BitRates._40, _

                                                              LAME.BitRates._48,
_

                                                            
 LAME.BitRates._56, _

                                                              LAME.BitRates._64,
_

                                                             
LAME.BitRates._80, _

                                                              LAME.BitRates._96,
_

                                                              LAME.BitRates._112,
_

                                                              LAME.BitRates._128,
_

                                                              LAME.BitRates._144,
_

                                                              LAME.BitRates._160,
_

                                                              LAME.BitRates._192,
_

                                                              LAME.BitRates._224,
_

                                                              LAME.BitRates._256,
_

                                                              LAME.BitRates._320}

        'Cargo los valores que puedo utilizar cuando creo un archivo MP3 con Bitrate variable
        Dim
ValoresCalidadPersonalizadaVBR()
As
LAME.CalidadVBR
= {LAME.CalidadVBR._0, _

                                                                  
LAME.CalidadVBR._1, _

                                                                  
LAME.CalidadVBR._2, _

                                                                  
LAME.CalidadVBR._3, _

                                                                  
LAME.CalidadVBR._4, _

                                                                  
LAME.CalidadVBR._5, _

                                                                  
LAME.CalidadVBR._6, _

                                                                  
LAME.CalidadVBR._7, _

                                                                  
LAME.CalidadVBR._8, _

                                                                  
LAME.CalidadVBR._9}

        Private Sub
cmdConvertir_Click(
ByVal
sender
As System.Object,
ByVal e As System.EventArgs) Handles
cmdConvertir.Click
                'El archivo que se va a crear (MP3)
               
Dim
ArchMP3 As New IO.FileStream(txtMP3.Text,
IO.FileMode.Create)



                'El archivo que se va a abrir para convertir (WAV)
                Dim ArchAbrir As
New
IO.FileStream(txtWAV.Text, IO.FileMode.Open)

                'En esta variable se obtendra la cantidad de byte leidos desde el archivo WAV
                Dim Read As
Int32



                'Aqui guardo la informacion leida desde el archivo WAV
                Dim buff As
Byte()



                'Este objeto nos permitira convertir lo leido en WAV a MP3 y grabarlo en el archivo MP3
                Dim GrabadorMP3 As
ConversorMP3


                'Determino la configuracion que desea el usuario, para el archivo de salida (MP3)
                If optCalidadNormal.Checked = True Then



                       
GrabadorMP3 =
New ConversorMP3(ArchMP3,
LAME.Calidad.CalidadNormal)

               
ElseIf
optCalidadRegular.Checked = True Then

                        GrabadorMP3
=
New ConversorMP3(ArchMP3, LAME.Calidad.CalidadRegular)
                ElseIf optCalidadMuyBuena.Checked = True Then
                        GrabadorMP3
=
New ConversorMP3(ArchMP3,
LAME.Calidad.CalidadMuyBuena)

                ElseIf optCalidadPersonalizada.Checked = True Then


                        GrabadorMP3
=
New ConversorMP3(ArchMP3,
ValoresCalidadPersonalizada(tbCalidadPersonalizada.Value))



                ElseIf optCalidadPersonalizadaVBR.Checked = True Then


                        GrabadorMP3
=
New ConversorMP3(ArchMP3,
ValoresCalidadPersonalizadaVBR(tbCalidadPersonalizadaVBR.Value))



                End If

                'Redimensiono el buffer de lectura segun lo que el "GrabadorMP3" desea que le envie en cada llamada a convertir.
                ReDim buff(GrabadorMP3.TamañoDelBuffer - 1)

                'Inicializo las propiedades de la barra de progreso
                          With
pbProgresoDeLaCopia

                        
.Maximum = ArchAbrir.Length

                         .Value
= 0

                 End With


                    
'En los primeros 44 bytes se encuetra el encabezado del archivo (Esto no lo convierto a MP3)


                ArchAbrir.Seek(44,
IO.SeekOrigin.Begin)

                'Leo una porcion del archivo WAV

                Read
= ArchAbrir.Read(buff, 0, buff.Length)

                'Mientras no haya terminado de leer el archivo WAV, sigo "Leyendo WAV->Convirtiendo a MP3->Grabando en MP3"
                Do While (Read > 0)
                       
'Codifico lo leido del archivo WAV y lo grabo en el archivo MP3




                        GrabadorMP3.Write(buff,
0, Read)

                        'Leo una porcion del archivo WAV

                        Read
= ArchAbrir.Read(buff, 0, buff.Length)

                        'Actualizo la barra de prograso
                        pbProgresoDeLaCopia.Value
= pbProgresoDeLaCopia.Value + Read

                        'Atiendo otros mensajes, para que la aplicacion no parezca que se colgo

                        Application.DoEvents()

                Loop


                'Cierro todo
                GrabadorMP3.Close()

                ArchMP3.Close()

                ArchAbrir.Close()

                'Aviso que termine
                MsgBox("La
conversion ha finalizado", MsgBoxStyle.Information, "WAV TO MP3")

        End
Sub

        'AQUI VA EL RESTO DEL CODIGO QUE MANEJA CIERTOS ASPECTOS DE LA INTERFACE
End Class

Esta pequeña aplicación sólo es a modo de ejemplo, y presenta muy pocas validaciones, asi que debes mirar que lo que escribes en las cajas de texto sea correcto y que incluyan todo el path y la extension (Por ej: "c:\Prueba.wav" para el WAV y "c:\Musica\MP3\ArchConv.mp3"). En el caso del MP3 lo unico que debe existir es path, ya que el archivo se creara con el nombre que tu especifiques.

CUIDADO: Como dije antes no hay muchas validaciones, y si el archivo de destino existe se reemplazara sin preguntar, asi que cuidado ( ¡yo avise! ).

Conclusión
Podemos ver que para convertir el archivo WAV en MP3, lo unico que debemos hacer es ir leyendo el archivo WAV por trozos del tamaño que nos devuelve el objeto de la clase ConversorMP3, y luego enviar lo leido al mismo objeto, para que se encargue de codificarlo y grabarlo en MP3, hasta que se termine de leer el archivo WAV.
Un detalle importante es que antes de empezar a leer y enviar a convertir, debemos avanzar 44 bytes en el archivo WAV, ya que en esta zona se encuentra el encabezado del archivo, donde podemos obtener cierta informacion sobre el formato en el que esta
grabado el mismo. Si no tuvieramos esta precaucion, el archivo igualmente se convertiria en MP3, pero escuchariamos un pequeño ruido cuando arranca la cancion.

La clase ConversorMP3 tiene 3 sobrecargas, segun que tipo de conversion queremos. Tenemos la posibilidad de grabar en MP3 con 3 calidades predifinidas (Regular, Normal, Muy Buena), las cuales en realidad son a modo ilustrativo y de hecho podria haber muchas mas, ya que LAME soporta varias calidades predefinidas que yo no he implementado en este ejemplo. Por otro lado tenemos la posibilidad de especificar nosotros cual es el Bitrate que queremos para nuestro archivo MP3 y por ultimo tambien podemos decirle al conversor que queremos usar un Bitrate variable.
Para terminar, queria decir que aquí sólo se trata de brindar un introduccion a la utilización de lame_enc.dll y que aun queda mucho por investigar y aprender.
Hay muchos aspectos que no tienen que ver netamente con la programación sino que en realidad estan relacionados con ciertas cuestiones que tienen que ver con el sonido (Bitrates (Constantes y variables), Samplerate, Psicoacustica entre otros.

Justamente lo que yo queria lograr aqui, era birndar una forma simple de poder convertir en MP3 un archivo WAV, sin tener que conocer demasiado sobre el tema, por eso es que una de las opciones que brinda la clase ConversorMP3 es la de crear un archivo MP3 con calidad Normal, Regular o Muy buena, ya que creo que es mas entendible esto, que decir que la calidad es 112 Kbps, 128 Kbps o 192 Kbps, todos a 44100 Hz y en modo Joint Stereo. Para el quiere probar como influye el Bitrate en la calidad del sonido, que pruebe con un bitrate de 8 Kbps y luego con uno de 128 Kbps o superior, y notara que este un factor mas que importante en el resultado final.

martes, 26 de enero de 2010

Heroes 5 Temporada - Capitulo 17

House 6 Temporada - Capitulo 12

viernes, 22 de enero de 2010

Aplicaciones Distribuidas 3 Capas

INTRODUCCION:

Las aplicaciones han pasado por un proceso evolutivo enorme.
Desde su inicio con las aplicaciones monolíticas donde en una aplicación todo estaba ligado o mezclado por decirlo de alguna manera. Es decir, la interfaces de usuario, la lógica de cómo funcionaba la empresa y el manejo de la información almacenada y recuperada estaban juntas.
Luego la industria ha implementado un nuevo modelo de aplicaciones, las aplicaciones distribuidas cliente/servidor, que se convirtió en el estándar por un tiempo. Pero con la llegada de las aplicaciones web se
hacia necesario un nuevo estándar para la operaciones de los sistemas, y es por esto que se ha propuesto el modelo de las aplicaciones en n-capas.
Este modelo por lo general esta basado en un esquema de tres partes: Acceso, Lógica de negocios e interfaces de usuario. Aunque es posible continuar sub dividiendo este modelo en sub capas para una mayor flexibilidad en la distribución en el equipo de desarrollo y durante el mantenimiento.
Veamos en que consiste.

Arquitectura de aplicaciones con tres capas

Arquitectura de Aplicaciones en n-capas Se ha convertido en el estándar para el software empresarial. Se caracteriza por la descomposición de las aplicaciones.
    • Proporciona una escalabilidad, capacidad de administración y utilización de recursos mejorados.
    • Cada capa es un grupo de componentes que realiza una función específica.
    • Se puede actualizar una capa sin recompilar otras capas.
Por regla general, La capa de la presentación es una interfaz gráfica que muestra los datos a los usuarios.
La capa de la lógica de negocios es responsable de procesar los datos recuperados y enviarlos a la capa de presentación.
La capa de datos almacena los datos de la aplicación en un almacén persistente, tal como una base de datos relacional o archivos XML.
Se pueden alojar todas las capas en el mismo servidor, pero también es posible alojar cada capa en varios servidores.
Para Ilustrar un poco el concepto de las aplicaciones distribuidas, vamos a crear un pequeño proyecto de 3 capas de venta de películas. En dicha aplicación crearemos:
    1- un servicio web como capa de acceso (asumiendo que este sería el modelo utilizado por cada compañía).
    2- un servicio web para la lógica de negocios, que en realidad constará de dos, el primero para buscar la información brindada por los proveedores como director, actores, duración, etc. Y otro para conseguir las críticas de algún sitio dedicado a esos fines.
    3- Un cliente que compre películas tomando en consideración la información que le brindamos al respecto.
Y para hacerlo más interesante vamos a extender un poco el artículo de nuestro colega Andrés Faya de escribir aplicaciones en distintos lenguajes, para implementar nuestros Web service de igual forma, uno en C# y otro en J# y Nuestra capa de acceso y el cliente van a funcionar bajo el lenguaje de Visual Basic.Net.

Nota: Las aplicaciones distribuidas no necesariamente tienen que estar construidas en base a los web service, estos se utilizan cuando se requiere que la información sea compatible entre plataformas. En caso en que está no sea la prioridad entonces es opcional utilizarla o implementar la distribución de la aplicación por medio de Remoting.Net.
La primera capa que vamos a desarrollar es la capa de acceso a datos. De esta capa cabe mencionar que es recomendable utilizar los Store Procedure en vez de ejecutar las sentencias directas desde el programa.
Se preguntaran por qué? Simple cuando ejecutas las sentencias directas contra la base de datos, toda la información referente a la sentencia viaja a través de la red, consumiendo mas ancho de banda y dejando a merced de algún sniffer la seguridad de nuestra base de datos…piensen como!!!.
La principal ventaja además de reducir los riesgos antes mencionados, es que los procedure son precompilados y funcionan de forma tipados, es decir, que solo reciben los parámetros que tienen preestablecidos y de los tipos específicos para cada caso, sumándole que se ejecutan mucho mas rápido que ejecutando sentencias desde el programa y le dejamos al servidor de base de datos procesar la información creando un mayor balance en la carga de trabajo en nuestra red.
Antes de mostrar el código de la capa de acceso me parece necesario, hacer mención de los mecanismos que nos permiten utilizar los web service que son la base de nuestro ejemplo.
    • Lo primero es que necesitamos conocer son UDDI y DISCO que permite a los clientes de los servicios acceder a la funcionalidad que expone un servicio web dándole la ubicación del servicio remoto.
    • Luego de conocer la ubicación, los clientes necesitan tener información de cómo interactuar con dichos servicios.
    • El formato de los mensajes que intercambiarán el cliente y los servicios web como fundamento de comunicación debe tener un mecanismo de codificación y decodificación.
Para estos fines es indiscutible que el lenguaje a utilizar es el XML que se ha convertido en el estándar de intercambio de información, por permitir la interoperatividad entre plataformas y por usar un sistema de tipo común. Una vez conocidas estas reglas entonces procedemos a codificar.

Código de capa de acceso:

Cabe destacar el uso de la clase Microsoft.ApplicationBlocks.Data versión 1 que nos permite acceso a datos de forma eficaz y rápida...aunque ya existe una segunda versión para los interesados.

Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.OleDb
Imports System.Security
Imports System.Configuration
Imports Microsoft.ApplicationBlocks.Data
Imports System.Web.Services
<System.Web.Services.WebService(Namespace:="http:"//tempuri.org/CapaAcceso/Service1")> _
Public Class CapaAcceso
    Inherits System.Web.Services.WebService
 
#Region " Web Services Designer Generated Code "
 
    Public Sub New()
        MyBase.New()
 
        'This call is required by the Web Services Designer.
        InitializeComponent()
 
        'Add your own initialization code after the InitializeComponent() call
 
    End Sub
 
    'Required by the Web Services Designer
    Private components As System.ComponentModel.IContainer
 
    'NOTE: The following procedure is required by the Web Services Designer
    'It can be modified using the Web Services Designer.  
    'Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container
    End Sub
 
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        'CODEGEN: This procedure is required by the Web Services Designer
        'Do not modify it using the code editor.
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub
 
#End Region
 
    Private sqlconn As String = ("server=(local);database=Peliculas;uid=sql;pwd=mar45245wqek")
    Private ds As New DataSet
    Private i As Integer
 
    'Lo primero que debemos saber es que para hacer que una función o procedimiento
    'Sea interpretado como un método que puede ser consumido por un cliente web es 
    'necesario, declararlo con esta finalidad, es decir declararlo como un método web
    'Para ello,utilizamos la palabra reservada WebMetohd entre los simbolos de "<>"
    'Por lo que quedaría así "< WebMethod() >" 
'Lo segundo que debos saber es que para sobre cargar los WebMethod utilizamos 
    'la propiedad llamada MessageName para que el servicio web distinga estre estas
    'funciones o procedimiento.
 
    'Esto no quiere decir que, para llamar dichas funciones usemos el nombre designado
    'en el MessageName, se invocan igual que un método sobre cargado en una clase normal.
    
    'Lo siguiente a tomar en cuenta es el nivel de acceso. Pues al igual que en un clase
    'cualquiera los niveles de private, public tienen los mismo efectos. Es decir, si desea
    'que un método solo sea accesible desde esta clase entonces lo declaras private, pero si
    'desea utilizarlo como un servicio web, entonces debe ser public para poder ser accesible
    'desde las instancias. 
'Sobre cargo la función Query para los diversos caso que pueda necesitar. 
'La función del query es para los procedure que retornar valores( Select).
    <WebMethod(MessageName:="cero")> Public Function Query(ByVal procedure As String) As DataSet
        ds = SqlHelper.ExecuteDataset(sqlconn, CommandType.StoredProcedure, procedure)
        Return ds
    End Function
 
    <WebMethod(MessageName:="primero")> Public Function Query(ByVal conexion As String, ByVal procedure As String) As DataSet
        ds = SqlHelper.ExecuteDataset(conexion, CommandType.StoredProcedure, procedure)
        Return ds
    End Function
    <WebMethod(MessageName:="segundo")> Public Function Query(ByVal conexion As String, ByVal procedure As String, ByVal parametros() As Object) As DataSet
        ds = SqlHelper.ExecuteDataset(conexion, CommandType.StoredProcedure, procedure, parametros)
        Return ds
    End Function
    <WebMethod(MessageName:="tercero")> Public Function Query(ByVal procedure As String, ByVal parametros() As SqlParameter) As DataSet
        Try
            ds = SqlHelper.ExecuteDataset(sqlconn, CommandType.StoredProcedure, procedure, parametros)
            Return ds
        Catch ex As SqlException
            MsgBox(ex.Procedure & ", " & ex.LineNumber & ", " & ex.Message)
        End Try
    End Function
    'sobre cargo QueryInteger por si necesito que el procesudre me retorne por ejemplo un id.   
    <WebMethod(MessageName:="primer")> Public Function QueryInteger(ByVal procedure As String) As Int32
        Try
            i = SqlHelper.ExecuteScalar(sqlconn, CommandType.StoredProcedure, procedure)
            Return i
        Catch ex As Exception
            Trace.Write(ex.Message)
        End Try
    End Function
    <WebMethod(MessageName:="second")> Public Function QueryInteger(ByVal procedure As String, ByVal parametros() As SqlParameter) As Integer
        Try
            i = SqlHelper.ExecuteScalar(sqlconn, CommandType.StoredProcedure, procedure, parametros)
            Return i
        Catch ex As Exception
            Trace.Write(ex.Message)
        End Try
    End Function
    'Sobre cargo la función NoQuery para los procedure con  (Insert, delete, update)
    <WebMethod(MessageName:="1")> Public Function NoQuery(ByVal procedure As String, ByVal parametros() As SqlParameter)
        Try
            SqlHelper.ExecuteNonQuery(sqlconn, CommandType.StoredProcedure, procedure, parametros)
        Catch ex As Exception
            Trace.Write(ex.Message)
        End Try
    End Function
    <WebMethod(MessageName:="2")> Public Function NoQuery(ByVal conexion As String, ByVal procedure As String, ByVal parametros() As Object) As Integer
        Try
            i = SqlHelper.ExecuteNonQuery(conexion, CommandType.StoredProcedure, procedure, parametros)
            Return i
        Catch ex As Exception
            Trace.Write(ex.Message)
        End Try
    End Function
    <WebMethod(MessageName:="3")> Public Function NoQuery(ByVal conexion As String, ByVal procedure As String) As Integer
        Try
            i = SqlHelper.ExecuteNonQuery(conexion, CommandType.StoredProcedure, procedure)
            Return i
        Catch ex As Exception
            Trace.Write(ex.Message)
        End Try
    End Function
End Class


Conclusión

Esta ha sido la primera parte del artículo, en la siguinte parte vamos a crear los web services. Como habíamos dicho serán creados en lenguajes diferentes.
Compile una vez y uselo siempre aunque sea en otra plataforma,"Cosa que no pasa". Aquí compilamos una vez y podemos usar esas clases en otros lenguajes y al tratarse de un web service en otras plataformas de forma real.