Giúp File Đọc Số ra Âm thanh dùng trong Khu Cách Ly COVID-19

Liên hệ QC

hunglam123

Thành viên bị đình chỉ hoạt động
Thành viên bị đình chỉ hoạt động
Tham gia
31/3/20
Bài viết
180
Được thích
43
Em xin chào cả nhà. Hiện tại bên công ty đã đều em đến 1 trung tâm quản lý các bệnh nhân cách ly tại Quận 9 ( Em làm bên nghành y tế ) . hằng ngày cứ 3 lần em đều thấy mấy anh Quân Nhân phải đọc số để Đo nhiệt độ và phát cơm mổi ngày. Nên em nghỉ ra ý tưởng lập 1 file excel để khi nhập số vào nhấn nút đọc thì âm thanh từ máy tính sẽ phát ra Loa bên ngoài. để mọi người như đều nghe thấy
Giao diện như thế này


Vùng B4:B15 là vùng lấy đường dẫn File âm thanh. Tức nhiên em đã thu âm các số 0,1,2,3..9 và câu mở đầu và câu kết thúc
Tại ô F4 em muốn nhập số bệnh nhân vào
Tại ô G4 em nhập số lần lặp lại
Nhấn nút Đọc số thì chương trình đọc ra âm thanh.
Ví dụ gõ số 0123 thì Loa phát ra " Xin mời quý khách mang số . không Một Hai Ba . Vui lòng đến quầy "

Do kiến thức em hạn hẹp em chỉ tạo mỗi được giao diện , và em đã thu âm và đã đổi sang đươi WAV hết, và các câu lệnh lấy đường dẫn âm thanh. Phần đọc số em chưa biết dùng câu lệnh gì.
rất mong mọi người cùng cac bang Quản Trị Viên giúp đở để chương trình đưa ra hoạt động giúp đở cộng đồng vướt qua mùa dịch này. Một lần nữa em xin chân thành cảm ơn
 
Lần chỉnh sửa cuối:
Hàm này chưa chuẩn anh ơi, anh xem trong file đính kèm nhé.

Chỗ tải file thì link kia trả về là tập tin audio luôn nên theo em dùng URLDownloadToFile API cho nhanh.

Vậy quy trình với TTS của Google đại khái: encode chuỗi và ghép vào link TTS, tải file audio, mở file audio.
--------------------------


Vì bác batman1 đang "show" nên "show" cho màu mè thôi.

----------------------
PHP:
Function EncodeURL(strText)
    If Application.Version >= 15 Then
    EncodeURL= Application.EncodeURL(strText)
    Else
    Static objHtmlfile As Object
    If objHtmlfile Is Nothing Then
        Set objHtmlfile = VBA.CreateObject("htmlfile")
        objHtmlfile.parentWindow.execScript "function encode(s) {return encodeURIComponent(s)}", "jscript"
    End If
    EncodeURL = objHtmlfile.parentWindow.encode(strText)
    End If
End Function
 
Upvote 0
Tôi đã viết rõ là đơn giản nhất là dùng PlaySound. Và đổi WMA thành WAV vì PlaySound không phục vụ WMA.

Còn máy vạn năng thì là mciSendString rồi.

Về mciSendString tôi cũng đã viết hơn 7,5 năm rồi.

------------
Về bài #8 thì sao lại khởi động chrome? Thứ nhất là khởi động trình duyệt sẽ lâu, thứ 2 là không phải ai cũng dùng chrome. Tôi trước kia dùng IE, bây giờ dùng Firefox. Chưa bao giờ dùng trình duyệt nào khác.

Trong tập tin đính kèm tôi không khởi động trình duyệt nào cả.

Tôi cũng tự viết hàm utf8_encode.

google text to speech rất giỏi, nhưng nhiều khi dịch vẫn không chuẩn, nhiều khi dịch rất buồn cười. Nhưng cho nhu cầu chủ đề này hoàn toàn được.

Những ai chưa biết cách lấy tập tin mp3 của google về máy thì xem tôi "biểu diễn" trong tập tin. :D

Em đã test file thầy rất là hay. không phải ghi âm trước. CHo em ví dụ em có 100 bệnh nhân em muốn copy mã số vào vùng B5:B105 rồi nhấn cho nó đọc lần lượt ( B5,b6...B100) để em đi làm chuyện khác chứ . thì em phải thêm For Next vào mục nào hả thầy
1585882401972.png
 
Upvote 0
Hiện tại FPT đã và đang phát triển API để đọc chữ Tiếng Việt và nhận dạng Tiếng Việt qua giọng nói. Giọng đọc của bản v5 khá chuẩn hơn so với v4 trước đó. Tuy nhiên với tk miễn phí thì có giới hạn (10.000 kí tự / request, 60request/ phút).
 
Upvote 0
Em đã test file thầy rất là hay. không phải ghi âm trước. CHo em ví dụ em có 100 bệnh nhân em muốn copy mã số vào vùng B5:B105 rồi nhấn cho nó đọc lần lượt ( B5,b6...B100) để em đi làm chuyện khác chứ . thì em phải thêm For Next vào mục nào hả thầy
-------------------------


Ở trên tôi đã nói Bác batman1 viết code quá "amater", vì vậy rất khó áp dụng.




-------------------------
Code bài trên:
Copy code dưới thay thế code trong module Player_MCI



-------------
JavaScript:
'                    _,
' ___   _   _  _ ___(_)
'/ __| / \ | \| | _ | |
'\__ \/ \ \| \\ | _ \ |
'|___/_/ \_|_|\_|___/_|
'

'Play Music
Option Explicit
#If VBA7 Then
  Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
  Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
  Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
#End If
'//////////////////////////////////////////////////////////////
#If Win64 Then
  Public Glb_TimerID As LongPtr
#Else
  Public Glb_TimerID As Long
#End If
#If VBA7 Then
  Private Declare PtrSafe Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long
  Private Declare PtrSafe Function mciGetErrorString Lib "winmm.dll" Alias "mciGetErrorStringA" (ByVal dwError As Long, ByVal lpstrBuffer As String, ByVal uLength As Long) As Long
  Private Declare PtrSafe Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameW" (ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long
#Else
  Private Declare  Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long
  Private Declare  Function mciGetErrorString Lib "winmm.dll" Alias "mciGetErrorStringA" (ByVal dwError As Long, ByVal lpstrBuffer As String, ByVal uLength As Long) As Long
  Private Declare  Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameW" (ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long
#End If
'//////////////////////////////////////////////////////////////////////////////////

Type PlayerPlaying
  CodeName As String
  ItemIndex As Long
  ItemLength As Long
  ItemName As String
  ItemArtor As Long
  List As Variant
  ListCount As Long
  ListIndex As Long
  Loop As Long
  IndexLoop As Long
  Shuffle As Boolean
  Prioritize As Boolean
  Volume As Long
  Speed As Double
  LoopWait As Long
  Position As Long
End Type
'//////////////////////////////////////////////////////////////////////////////////
Private PNP As PlayerPlaying, DoTime As Date
'//////////////////////////////////////////////////////////////////////////////////
Private Row As Long, Col As Long
Public Sub Player_End()
  On Error Resume Next
  If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  Application.OnTime DoTime, "'" & ThisWorkbook.Name & "'!Player_DoTime", , False
  Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!CloseAllAudio", , False
  Call CloseAllAudio
  DoTime = 0: Row = 0: Col = 0
  End
End Sub
Public Sub MakeFileAndPlay()
  Static Tmp As String
  Dim WS As Worksheet
  Dim Arr(), K, I, Text As String
  Dim RNG As Range, iStep As Long, LR As Long
  Set WS = Worksheets("data")

  If Row = 0 Then
    Set RNG = Selection
    If VBA.TypeName(RNG) <> "Range" Then Exit Sub
    Row = RNG.Row: Col = RNG.Column
  Else
    Row = Row + 1
  End If

  Const StartColumn = "B"
  Const StartRow = 5
  LR = WS.Range(StartColumn & 1000).End(3).Row
  If Row > LR Then GoTo Ends

  If Col <> Columns(StartColumn).Column Or Row < StartRow Or LR < StartRow Then
    Row = 0: Col = 0: Exit Sub
  End If

  WS.Range(StartColumn & StartRow).Resize(LR).Interior.Color = VBA.vbWhite

  With WS.Range(StartColumn & Row)
    Text = .Value
    .Interior.Color = VBA.vbYellow
  End With

  If Text = "" Or Not VBA.IsNumeric(Text) Then GoTo Ends
  If Tmp = "" Then
    Tmp = PATH_SYS_TEMP & "\MCIPlay\"
    CreateFolder Tmp
    ConvertBase64ToFile Tmp & "cau_mo_dau.wma", Base64_File_cau_mo_dau_wma
    ConvertBase64ToFile Tmp & "so_0.wma", Base64_File_so_0_wma
    ConvertBase64ToFile Tmp & "so_1.wma", Base64_File_so_1_wma
    ConvertBase64ToFile Tmp & "so_2.wma", Base64_File_so_2_wma
    ConvertBase64ToFile Tmp & "so_3.wma", [g6].Value
    ConvertBase64ToFile Tmp & "so_4.wma", Base64_File_so_4_wma
    ConvertBase64ToFile Tmp & "so_5.wma", Base64_File_so_5_wma
    ConvertBase64ToFile Tmp & "so_6.wma", Base64_File_so_6_wma
    ConvertBase64ToFile Tmp & "so_7.wma", Base64_File_so_7_wma
    ConvertBase64ToFile Tmp & "so_8.wma", Base64_File_so_8_wma
    ConvertBase64ToFile Tmp & "so_9.wma", Base64_File_so_9_wma
    ConvertBase64ToFile Tmp & "Vui_long_den_quay.wma", Base64_File_cau_ket_wma
  End If
  K = 1: ReDim Preserve Arr(1 To K)
  Arr(1) = Tmp & "cau_mo_dau.wma"
  For I = 1 To Len(Text)
    K = K + 1: ReDim Preserve Arr(1 To K)
    Arr(K) = Tmp & "so_" & VBA.Mid(Text, I, 1) & ".wma"
  Next
  K = K + 1: ReDim Preserve Arr(1 To K)
  Arr(K) = Tmp & "Vui_long_den_quay.wma"
  '------------------------------
  Player_Intialize Arr, _
                   iLoop:=[C5].Value, _
                   Speed:=1.1, _
                   Volume:=100
Ends:
  If iStep > LR Then Call Player_End
End Sub
'==============================================================
Private Function PATH_SYS_TEMP() As String
  PATH_SYS_TEMP = VBA.IIf(VBA.Environ("tmp") <> "", VBA.Environ("tmp"), VBA.Environ("temp"))
End Function
Public Sub Player_Intialize( _
                      ByVal List As Variant, _
                   Optional iLoop As Long = 0, _
                   Optional WaitLoop As Long = 1, _
                   Optional bShuffle As Boolean = False, _
                   Optional Speed As Double = 1.1, _
                   Optional Volume As Byte = 100, _
             Optional ByVal NewAliasName As String = "FileMusic")
  If Not VBA.IsArray(List) Then Exit Sub
  Dim I As Long, J As Long
  On Error Resume Next
  If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  Application.OnTime DoTime, "'" & ThisWorkbook.Name & "'!Player_DoTime", , False
  Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!CloseAllAudio", , False
  Call CloseAllAudio
Make:
  PNP.List = List
  PNP.Loop = iLoop
  PNP.LoopWait = WaitLoop
  PNP.Shuffle = bShuffle
  PNP.CodeName = NewAliasName
  PNP.Speed = VBA.IIf(Speed < 0.5, 0.5, VBA.IIf(Speed > 4, 4, Speed))
  PNP.Volume = VBA.IIf(Volume < 0, 0, VBA.IIf(Volume > 100, 100, Volume))
  Dim Item, L As Long, NewList()
  For Each Item In PNP.List
    If VBA.TypeName(Item) = "String" And Len(Item) > 5 Then
      L = L + 1
      ReDim Preserve NewList(1 To L)
      NewList(L) = ShortPath(Item)
    End If
  Next
  If L > 0 Then
    PNP.IndexLoop = 0
    PNP.ItemIndex = 0
    PNP.ListCount = L
    PNP.List = NewList
    Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!Player_ListPlayNext"
  End If
End Sub
'//////////////////////////////////////////////////////////////////////////////////
Public Sub Player_ListPlayNext()
  DoEvents
  On Error Resume Next
  If Not VBA.IsArray(PNP.List) Then Call Player_End: Exit Sub
  Dim I As Long, Idx As Long, R As Long

  DoTime = VBA.Now
  If PNP.Shuffle Then
    VBA.Randomize
Rnd: Idx = Int(PNP.ListCount * Rnd + 1)
    If Idx = PNP.ItemIndex Then GoTo Rnd
  Else
    Idx = PNP.ItemIndex + 1
    If Idx > PNP.ListCount Then
      PNP.Speed = 1.5
      Application.Wait VBA.Now + VBA.TimeSerial(0, 0, PNP.LoopWait)
      Idx = 1: PNP.IndexLoop = PNP.IndexLoop + 1
      If PNP.Loop = 0 Or PNP.IndexLoop > PNP.Loop Then DoTime = 0
    End If
  End If
  If DoTime > 0 Then
    PNP.ItemIndex = Idx
    PNP.ItemName = PNP.List(Idx)
    Call FileOpen
    DoTime = (PNP.ItemLength - 1500)
    DoTime = VBA.IIf(DoTime < 1000, 0, DoTime / 1000 / PNP.Speed)
    DoTime = VBA.Now + VBA.TimeSerial(0, 0, DoTime)
    Application.OnTime DoTime, "'" & ThisWorkbook.Name & "'!Player_DoTime"
  Else
    Application.OnTime VBA.Now + VBA.TimeSerial(0, 0, 2), "'" & ThisWorkbook.Name & "'!MakeFileAndPlay"
  End If
End Sub



Public Sub FileOpen()
  DoEvents
  Dim lRet&, sError As String * 255
  Call mciSendString("Close all", "", 0, 0)
  Call mciSendString("Close " & PNP.CodeName, "", 0, 0)
  lRet = mciSendString("open """ & PNP.ItemName & """ alias " & PNP.CodeName, "", 0, 0)
  If PNP.Speed <= 0 Then PNP.Speed = 1
  If PNP.Volume <= 0 Then PNP.Volume = 100
  If lRet <> 0 Then mciGetErrorString lRet, sError, 255: Exit Sub
  Call mciSendString("Play " & PNP.CodeName, "", 0, 0)
  Call mciSendString("set " & PNP.CodeName & " Speed " & CStr(Int(PNP.Speed * 1000)), "", 0, 0)
  Call mciSendString("setaudio " & PNP.CodeName & " volume to " & PNP.Volume * 10, "", 0, 0)
  Call mciSendString("Play " & PNP.CodeName, "", 0, 0)
  PNP.ItemLength = AudioGetLength()
End Sub




Public Sub Player_DoTime()
  If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  Glb_TimerID = SetTimer(0&, 0&, 300, AddressOf Player_CheckPlayNext)
End Sub

Public Sub Player_CheckPlayNext()
  DoEvents
  Dim T1 As Long
  T1 = AudioGetCurPos()
  If T1 = 0 Then If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  If T1 >= PNP.ItemLength - 100 Then
    If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
    Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!Player_ListPlayNext"
  End If
End Sub
'//////////////////////////////////////////////////////////////////////////////////

Function AudioGetLength() As Long
    Dim lRet&, S As String * 255: S = VBA.Space(255)
    On Error Resume Next
    lRet = mciSendString("status " & PNP.CodeName & " length", S, 255, 0)
    If lRet = 0 Then AudioGetLength = CLng(S)
End Function
'//////////////////////////////////////////////////////////////////////////////////
Function AudioGetCurPos() As Long
  DoEvents
  Dim lRet&, S As String * 255: S = VBA.Space(255)
  On Error Resume Next
  lRet = mciSendString("status " & PNP.CodeName & " position wait", S, 255, 0)
  If lRet = 0 Then AudioGetCurPos = CLng(S)
End Function

'//////////////////////////////////////////////////////////////////////////////////
Sub CloseAllAudio()
  Call mciSendString("Close all", "", 0, 0)
  On Error Resume Next
End Sub
'//////////////////////////////////////////////////////////////////////////////////
Sub ConvertBase64ToFile(strFilePath As String, strBase64 As String)
  If VBA.Dir(strFilePath, vbSystem) <> "" Then Exit Sub
  Const UseBinaryStreamType = 1
  Const SaveWillCreateOrOverwrite = 2
  Dim streamOutput, xmlDoc: Set streamOutput = CreateObject("ADODB.Stream")
  #If Win64 Then
    Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")
  #Else
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
  #End If
  Dim xmlElem: Set xmlElem = xmlDoc.createElement("tmp")
  xmlElem.DataType = "bin.base64"
  xmlElem.Text = strBase64
  streamOutput.Open
  streamOutput.Type = UseBinaryStreamType
  streamOutput.Write = xmlElem.nodeTypedValue
  streamOutput.SaveToFile strFilePath, SaveWillCreateOrOverwrite
  Set streamOutput = Nothing
  Set xmlDoc = Nothing
  Set xmlElem = Nothing
End Sub

Private Function CreateFolder(ByVal FolderPath As String, Optional ByRef FileSystem As Object) As Boolean
  Dim FolderArray, Tmp$, I As Integer, UB As Integer, tFolder$
  tFolder = FolderPath
  If VBA.Right(tFolder, 1) = "\" Then tFolder = VBA.Left(tFolder, VBA.Len(tFolder) - 1)
  If tFolder Like "\\*\*" Then tFolder = VBA.Strings.Replace(tFolder, "\", "@", 1, 3)
  FolderArray = VBA.Split(tFolder, "\")
  FolderArray(0) = VBA.Strings.Replace(FolderArray(0), "@", "\", 1, 3)
  On Error GoTo Ends
  If FileSystem Is Nothing Then
    Set FileSystem = VBA.CreateObject("Scripting.FileSystemObject")
  End If
  UB = UBound(FolderArray)
  With FileSystem
    For I = 0 To UB
      Tmp = Tmp & FolderArray(I) & "\"
      If Not .FolderExists(Tmp) Then .CreateFolder (Tmp)
      CreateFolder = (I = UB) And Len(FolderArray(I)) > 0 And FolderArray(I) <> " "
    Next
  End With
Ends:
End Function
'//////////////////////////////////////////////////////////////////////////////////
Function ShortPath(ByVal LongPath As String) As String
  Dim ret&, Buff As String * 512
  ret = GetShortPathName(StrConv(LongPath, 64), Buff, 512)
  ShortPath = Left(StrConv(Buff, 128), ret)
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
Hiện tại FPT đã và đang phát triển API để đọc chữ Tiếng Việt và nhận dạng Tiếng Việt qua giọng nói. Giọng đọc của bản v5 khá chuẩn hơn so với v4 trước đó. Tuy nhiên với tk miễn phí thì có giới hạn (5.000 kí tự / request, 60request/ phút).
Không mượt và ngọt như API của Google.
 
Upvote 0
Về bài #8 thì sao lại khởi động chrome? Thứ nhất là khởi động trình duyệt sẽ lâu

--------------------------------

Hôm trước có một bạn cũng hỏi vấn đề đọc tiếng Việt.

Và tôi cũng có trả lời bên đấy, rất đầy đủ.

Nếu bác có thời gian thì ghé qua xem lại

https://www.giaiphapexcel.com/diend...-tiếng-việt-trong-excel-ạ.148438/#post-959974
 
Upvote 0
-------------------------


Ở trên tôi đã nói Bác batman1 viết code quá "amater", vì vậy rất khó áp dụng.




-------------------------
Code bài trên:
Copy code dưới thay thế code trong module Player_MCI



-------------
JavaScript:
'                    _,
' ___   _   _  _ ___(_)
'/ __| / \ | \| | _ | |
'\__ \/ \ \| \\ | _ \ |
'|___/_/ \_|_|\_|___/_|
'

'Play Music
Option Explicit
#If VBA7 Then
  Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
  Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
#Else
  Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
#End If
'//////////////////////////////////////////////////////////////
#If Win64 Then
  Public Glb_TimerID As LongPtr
#Else
  Public Glb_TimerID As Long
#End If
#If VBA7 Then
  Private Declare PtrSafe Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long
  Private Declare PtrSafe Function mciGetErrorString Lib "winmm.dll" Alias "mciGetErrorStringA" (ByVal dwError As Long, ByVal lpstrBuffer As String, ByVal uLength As Long) As Long
  Private Declare PtrSafe Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameW" (ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long
#Else
  Private Declare  Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Long, ByVal hwndCallback As Long) As Long
  Private Declare  Function mciGetErrorString Lib "winmm.dll" Alias "mciGetErrorStringA" (ByVal dwError As Long, ByVal lpstrBuffer As String, ByVal uLength As Long) As Long
  Private Declare  Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameW" (ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long
#End If
'//////////////////////////////////////////////////////////////////////////////////

Type PlayerPlaying
  CodeName As String
  ItemIndex As Long
  ItemLength As Long
  ItemName As String
  ItemArtor As Long
  List As Variant
  ListCount As Long
  ListIndex As Long
  Loop As Long
  IndexLoop As Long
  Shuffle As Boolean
  Prioritize As Boolean
  Volume As Long
  Speed As Double
  LoopWait As Long
  Position As Long
End Type
'//////////////////////////////////////////////////////////////////////////////////
Private PNP As PlayerPlaying, DoTime As Date
'//////////////////////////////////////////////////////////////////////////////////
Private Row As Long, Col As Long
Public Sub Player_End()
  On Error Resume Next
  If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  Application.OnTime DoTime, "'" & ThisWorkbook.Name & "'!Player_DoTime", , False
  Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!CloseAllAudio", , False
  Call CloseAllAudio
  DoTime = 0: Row = 0: Col = 0
  End
End Sub
Public Sub MakeFileAndPlay()
  Static Tmp As String
  Dim WS As Worksheet
  Dim Arr(), K, I, Text As String
  Dim RNG As Range, iStep As Long, LR As Long
  Set WS = Worksheets("data")

  If Row = 0 Then
    Set RNG = Selection
    If VBA.TypeName(RNG) <> "Range" Then Exit Sub
    Row = RNG.Row: Col = RNG.Column
  Else
    Row = Row + 1
  End If

  Const StartColumn = "B"
  Const StartRow = 5
  LR = WS.Range(StartColumn & 1000).End(3).Row
  If Row > LR Then GoTo Ends

  If Col <> Columns(StartColumn).Column Or Row < StartRow Or LR < StartRow Then
    Row = 0: Col = 0: Exit Sub
  End If

  WS.Range(StartColumn & StartRow).Resize(LR).Interior.Color = VBA.vbWhite

  With WS.Range(StartColumn & Row)
    Text = .Value
    .Interior.Color = VBA.vbYellow
  End With

  If Text = "" Or Not VBA.IsNumeric(Text) Then GoTo Ends
  If Tmp = "" Then
    Tmp = PATH_SYS_TEMP & "\MCIPlay\"
    CreateFolder Tmp
    ConvertBase64ToFile Tmp & "cau_mo_dau.wma", Base64_File_cau_mo_dau_wma
    ConvertBase64ToFile Tmp & "so_0.wma", Base64_File_so_0_wma
    ConvertBase64ToFile Tmp & "so_1.wma", Base64_File_so_1_wma
    ConvertBase64ToFile Tmp & "so_2.wma", Base64_File_so_2_wma
    ConvertBase64ToFile Tmp & "so_3.wma", [g6].Value
    ConvertBase64ToFile Tmp & "so_4.wma", Base64_File_so_4_wma
    ConvertBase64ToFile Tmp & "so_5.wma", Base64_File_so_5_wma
    ConvertBase64ToFile Tmp & "so_6.wma", Base64_File_so_6_wma
    ConvertBase64ToFile Tmp & "so_7.wma", Base64_File_so_7_wma
    ConvertBase64ToFile Tmp & "so_8.wma", Base64_File_so_8_wma
    ConvertBase64ToFile Tmp & "so_9.wma", Base64_File_so_9_wma
    ConvertBase64ToFile Tmp & "Vui_long_den_quay.wma", Base64_File_cau_ket_wma
  End If
  K = 1: ReDim Preserve Arr(1 To K)
  Arr(1) = Tmp & "cau_mo_dau.wma"
  For I = 1 To Len(Text)
    K = K + 1: ReDim Preserve Arr(1 To K)
    Arr(K) = Tmp & "so_" & VBA.Mid(Text, I, 1) & ".wma"
  Next
  K = K + 1: ReDim Preserve Arr(1 To K)
  Arr(K) = Tmp & "Vui_long_den_quay.wma"
  '------------------------------
  Player_Intialize Arr, _
                   iLoop:=[C5].Value, _
                   Speed:=1.1, _
                   Volume:=100
Ends:
  If iStep > LR Then Call Player_End
End Sub
'==============================================================
Private Function PATH_SYS_TEMP() As String
  PATH_SYS_TEMP = VBA.IIf(VBA.Environ("tmp") <> "", VBA.Environ("tmp"), VBA.Environ("temp"))
End Function
Public Sub Player_Intialize( _
                      ByVal List As Variant, _
                   Optional iLoop As Long = 0, _
                   Optional WaitLoop As Long = 1, _
                   Optional bShuffle As Boolean = False, _
                   Optional Speed As Double = 1.1, _
                   Optional Volume As Byte = 100, _
             Optional ByVal NewAliasName As String = "FileMusic")
  If Not VBA.IsArray(List) Then Exit Sub
  Dim I As Long, J As Long
  On Error Resume Next
  If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  Application.OnTime DoTime, "'" & ThisWorkbook.Name & "'!Player_DoTime", , False
  Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!CloseAllAudio", , False
  Call CloseAllAudio
Make:
  PNP.List = List
  PNP.Loop = iLoop
  PNP.LoopWait = WaitLoop
  PNP.Shuffle = bShuffle
  PNP.CodeName = NewAliasName
  PNP.Speed = VBA.IIf(Speed < 0.5, 0.5, VBA.IIf(Speed > 4, 4, Speed))
  PNP.Volume = VBA.IIf(Volume < 0, 0, VBA.IIf(Volume > 100, 100, Volume))
  Dim Item, L As Long, NewList()
  For Each Item In PNP.List
    If VBA.TypeName(Item) = "String" And Len(Item) > 5 Then
      L = L + 1
      ReDim Preserve NewList(1 To L)
      NewList(L) = ShortPath(Item)
    End If
  Next
  If L > 0 Then
    PNP.IndexLoop = 0
    PNP.ItemIndex = 0
    PNP.ListCount = L
    PNP.List = NewList
    Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!Player_ListPlayNext"
  End If
End Sub
'//////////////////////////////////////////////////////////////////////////////////
Public Sub Player_ListPlayNext()
  DoEvents
  On Error Resume Next
  If Not VBA.IsArray(PNP.List) Then Call Player_End: Exit Sub
  Dim I As Long, Idx As Long, R As Long

  DoTime = VBA.Now
  If PNP.Shuffle Then
    VBA.Randomize
Rnd: Idx = Int(PNP.ListCount * Rnd + 1)
    If Idx = PNP.ItemIndex Then GoTo Rnd
  Else
    Idx = PNP.ItemIndex + 1
    If Idx > PNP.ListCount Then
      PNP.Speed = 1.5
      Application.Wait VBA.Now + VBA.TimeSerial(0, 0, PNP.LoopWait)
      Idx = 1: PNP.IndexLoop = PNP.IndexLoop + 1
      If PNP.Loop = 0 Or PNP.IndexLoop > PNP.Loop Then DoTime = 0
    End If
  End If
  If DoTime > 0 Then
    PNP.ItemIndex = Idx
    PNP.ItemName = PNP.List(Idx)
    Call FileOpen
    DoTime = (PNP.ItemLength - 1500)
    DoTime = VBA.IIf(DoTime < 1000, 0, DoTime / 1000 / PNP.Speed)
    DoTime = VBA.Now + VBA.TimeSerial(0, 0, DoTime)
    Application.OnTime DoTime, "'" & ThisWorkbook.Name & "'!Player_DoTime"
  Else
    Application.OnTime VBA.Now + VBA.TimeSerial(0, 0, 2), "'" & ThisWorkbook.Name & "'!MakeFileAndPlay"
  End If
End Sub



Public Sub FileOpen()
  DoEvents
  Dim lRet&, sError As String * 255
  Call mciSendString("Close all", "", 0, 0)
  Call mciSendString("Close " & PNP.CodeName, "", 0, 0)
  lRet = mciSendString("open """ & PNP.ItemName & """ alias " & PNP.CodeName, "", 0, 0)
  If PNP.Speed <= 0 Then PNP.Speed = 1
  If PNP.Volume <= 0 Then PNP.Volume = 100
  If lRet <> 0 Then mciGetErrorString lRet, sError, 255: Exit Sub
  Call mciSendString("Play " & PNP.CodeName, "", 0, 0)
  Call mciSendString("set " & PNP.CodeName & " Speed " & CStr(Int(PNP.Speed * 1000)), "", 0, 0)
  Call mciSendString("setaudio " & PNP.CodeName & " volume to " & PNP.Volume * 10, "", 0, 0)
  Call mciSendString("Play " & PNP.CodeName, "", 0, 0)
  PNP.ItemLength = AudioGetLength()
End Sub




Public Sub Player_DoTime()
  If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  Glb_TimerID = SetTimer(0&, 0&, 300, AddressOf Player_CheckPlayNext)
End Sub

Public Sub Player_CheckPlayNext()
  DoEvents
  Dim T1 As Long
  T1 = AudioGetCurPos()
  If T1 = 0 Then If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
  If T1 >= PNP.ItemLength - 100 Then
    If Glb_TimerID <> 0 Then KillTimer 0&, Glb_TimerID
    Application.OnTime VBA.Now, "'" & ThisWorkbook.Name & "'!Player_ListPlayNext"
  End If
End Sub
'//////////////////////////////////////////////////////////////////////////////////

Function AudioGetLength() As Long
    Dim lRet&, S As String * 255: S = VBA.Space(255)
    On Error Resume Next
    lRet = mciSendString("status " & PNP.CodeName & " length", S, 255, 0)
    If lRet = 0 Then AudioGetLength = CLng(S)
End Function
'//////////////////////////////////////////////////////////////////////////////////
Function AudioGetCurPos() As Long
  DoEvents
  Dim lRet&, S As String * 255: S = VBA.Space(255)
  On Error Resume Next
  lRet = mciSendString("status " & PNP.CodeName & " position wait", S, 255, 0)
  If lRet = 0 Then AudioGetCurPos = CLng(S)
End Function

'//////////////////////////////////////////////////////////////////////////////////
Sub CloseAllAudio()
  Call mciSendString("Close all", "", 0, 0)
  On Error Resume Next
End Sub
'//////////////////////////////////////////////////////////////////////////////////
Sub ConvertBase64ToFile(strFilePath As String, strBase64 As String)
  If VBA.Dir(strFilePath, vbSystem) <> "" Then Exit Sub
  Const UseBinaryStreamType = 1
  Const SaveWillCreateOrOverwrite = 2
  Dim streamOutput, xmlDoc: Set streamOutput = CreateObject("ADODB.Stream")
  #If Win64 Then
    Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")
  #Else
    Set xmlDoc = CreateObject("Microsoft.XMLDOM")
  #End If
  Dim xmlElem: Set xmlElem = xmlDoc.createElement("tmp")
  xmlElem.DataType = "bin.base64"
  xmlElem.Text = strBase64
  streamOutput.Open
  streamOutput.Type = UseBinaryStreamType
  streamOutput.Write = xmlElem.nodeTypedValue
  streamOutput.SaveToFile strFilePath, SaveWillCreateOrOverwrite
  Set streamOutput = Nothing
  Set xmlDoc = Nothing
  Set xmlElem = Nothing
End Sub

Private Function CreateFolder(ByVal FolderPath As String, Optional ByRef FileSystem As Object) As Boolean
  Dim FolderArray, Tmp$, I As Integer, UB As Integer, tFolder$
  tFolder = FolderPath
  If VBA.Right(tFolder, 1) = "\" Then tFolder = VBA.Left(tFolder, VBA.Len(tFolder) - 1)
  If tFolder Like "\\*\*" Then tFolder = VBA.Strings.Replace(tFolder, "\", "@", 1, 3)
  FolderArray = VBA.Split(tFolder, "\")
  FolderArray(0) = VBA.Strings.Replace(FolderArray(0), "@", "\", 1, 3)
  On Error GoTo Ends
  If FileSystem Is Nothing Then
    Set FileSystem = VBA.CreateObject("Scripting.FileSystemObject")
  End If
  UB = UBound(FolderArray)
  With FileSystem
    For I = 0 To UB
      Tmp = Tmp & FolderArray(I) & "\"
      If Not .FolderExists(Tmp) Then .CreateFolder (Tmp)
      CreateFolder = (I = UB) And Len(FolderArray(I)) > 0 And FolderArray(I) <> " "
    Next
  End With
Ends:
End Function
'//////////////////////////////////////////////////////////////////////////////////
Function ShortPath(ByVal LongPath As String) As String
  Dim ret&, Buff As String * 512
  ret = GetShortPathName(StrConv(LongPath, 64), Buff, 512)
  ShortPath = Left(StrConv(Buff, 128), ret)
End Function

đã thay code nó không đọc được luôn anh ơi.
 

File đính kèm

  • GoiSo - Copy.xlsb
    400.2 KB · Đọc: 7
Upvote 0
Bạn đừng có xóa dữ liệu ở cột G
 

File đính kèm

  • GoiSo.xlsb
    620.8 KB · Đọc: 43
Upvote 0
Bạn đừng có xóa dữ liệu ở cột G

Xin lỗi,.. bấm hết các nút mà không nghe thấy tiếng gì?tôi không hiểu gì cả?.. cột G là cái gì vậy sao loằng ngoằng thế.
Tôi tải tập tin bài 13 về chạy có tiếng bình thường, không phải thêm những cái loằng ngoằng như cột G..
Bài đã được tự động gộp:

Em đã test file thầy rất là hay. không phải ghi âm trước. CHo em ví dụ em có 100 bệnh nhân em muốn copy mã số vào vùng B5:B105 rồi nhấn cho nó đọc lần lượt ( B5,b6...B100) để em đi làm chuyện khác chứ . thì em phải thêm For Next vào mục nào hả thầy
View attachment 234616
Có thể thêm nút "DỪNG ĐỌC" và thêm một ô thời gian cách nhau mỗi số được không Bạn?
Ví dụ cứ 5 phút là lại đọc tiếp 1 số.. để cho mọi người còn chuẩn bị.
 
Lần chỉnh sửa cuối:
Upvote 0
Xin lỗi,.. bấm hết các nút mà không nghe thấy tiếng gì?tôi không hiểu gì cả?.. cột G là cái gì vậy sao loằng ngoằng thế.
Tôi tải tập tin bài 13 về chạy có tiếng bình thường, không phải thêm những cái loằng ngoằng như cột G..
-------------------------------

Thì bạn cứ dùng bài #13
 
Upvote 0
-------------------------------

Thì bạn cứ dùng bài #13
Nhìn code của bạn không rõ ràng, cứ như là virut ấy...
Bạn viết bài thì cũng phải có trách nhiệm một chút với bài viết của mình chứ.
Tôi cảm giác bạn viết để khoe nhiều hơn là bạn viết để cho mọi người sử dụng, vì bạn không quan tâm đến việc tôi nói là tập tin của bạn tôi không sử dụng được. mà không dùng được thì chắc chắn tôi sẽ dùng tập tin trong bài 13 rồi.. đâu cần bạn phải nhắc..
Chi bằng thay vì nhắc như vậy bạn nên dành thời gian để xem lại tập tin của bạn.
 
Upvote 0
-------------------------


Ở trên tôi đã nói Bác batman1 viết code quá "amater", vì vậy rất khó áp dụng.
Tôi trả lời cho một chủ đề cụ thể. Chỉ là đọc 1 số. Tôi không có ý định viết dài như bạn. Vì tôi đã từng viết ít ra là 3, 5 lần. Có đầy đủ các thủ tục open, play, pause, seek, close ... Người dùng chỉ phải tự viết thêm để gọi các thủ tục đó.

Nếu bạn lặp đi lặp lại là tôi "amater" thì xin chỉ rõ từng điểm để tôi học tập bạn được không? Nói miệng thì ai chả nói được.

Cái chuyện phát 1 danh sách liên tục là chuyện nhỏ như con thỏ. Tôi còn làm chuyện phát 2, 3 phim cùng lúc, phát cả phim và nhạc cùng lúc cơ. Nhưng chủ đề này người ta chỉ hỏi 1 vấn đề. Người ta hỏi và tôi đọc thấy là bạn chỉ cho vỏn vẹn 1 đoạn code dùng chrome phát audio từ google. Tôi thấy nó quá kém cỏi, vì đơn giản bạn không biết cách lấy mp3 về máy. Nếu biết thì bạn đã không dùng cách củ chuối rồi. Tôi chỉ chăm chú vào 1 việc cụ thể để cho bạn biết thôi. Tôi chỉ vạch ra một cách đi khác cho bài #8 của bạn thôi. Không viết nhiều.
 
Lần chỉnh sửa cuối:
Upvote 0
Nhìn code của bạn không rõ ràng, cứ như là virut ấy...
Bạn viết bài thì cũng phải có trách nhiệm một chút với bài viết của mình chứ.
Tôi cảm giác bạn viết để khoe nhiều hơn là bạn viết để cho mọi người sử dụng, vì bạn không quan tâm đến việc tôi nói là tập tin của bạn tôi không sử dụng được. mà không dùng được thì chắc chắn tôi sẽ dùng tập tin trong bài 13 rồi.. đâu cần bạn phải nhắc..
Chi bằng thay vì nhắc như vậy bạn nên dành thời gian để xem lại tập tin của bạn.
----------------------------


Thì bạn cứ dùng bài #13
Bài đã được tự động gộp:

đã thay code nó không đọc được luôn anh ơi.
-------------------------------------


Tôi quên hướng dẫn sử dụng

Bạn để con trỏ vào Ô trong cột B và click Đọc số

Nó sẽ đọc từ trên xuống
 
Lần chỉnh sửa cuối:
Upvote 0
Web KT
Back
Top Bottom