DTMF ... or it will be when you help me

IanMc

Well-Known Member
Licensed User
Longtime User
SoundPool seems to be a bit 'fire and forget'

Is there an easy way to test to see if a sound has finished playing?

I wrote this little class for playing dtmf tones ... and please don't say 'why don't you just use the built in phone function.... ' hehe :)

I want to do it this way because I

a) Want to totally learn how to play sounds

&

b) Because I want to take over the world! MUhahahaha!

too much coffee again :)

Here's me little test project:
 

Attachments

  • dtmf.zip
    32.1 KB · Views: 262

stevel05

Expert
Licensed User
Longtime User
If you don't really need to know when they've finished, if you set Maxstreams to 1 and then set the priority in descending order, it creates a queue, and plays the sounds one after the other.

Just another possible alternative.
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Really? I didn't know that!

That sounds like exactly what I'm looking for, create a queue.

Thanks Steve, I'll give that a try.
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Hmmm.... tried this, no luck so far, any ideas?
B4X:
'Class module
Sub Class_Globals
   Dim SP As SoundPool
   Dim iA(12) As Int
   Dim compare As String = "0123456789*#" 
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
   SP.Initialize(1)
   
   iA(0) = SP.Load(File.DirAssets, "dtmf_0.mp3")
   iA(1) = SP.Load(File.DirAssets, "dtmf_1.mp3")
   iA(2) = SP.Load(File.DirAssets, "dtmf_2.mp3")
   iA(3) = SP.Load(File.DirAssets, "dtmf_3.mp3")
   iA(4) = SP.Load(File.DirAssets, "dtmf_4.mp3")
   iA(5) = SP.Load(File.DirAssets, "dtmf_5.mp3")
   iA(6) = SP.Load(File.DirAssets, "dtmf_6.mp3")
   iA(7) = SP.Load(File.DirAssets, "dtmf_7.mp3")
   iA(8) = SP.Load(File.DirAssets, "dtmf_8.mp3")
   iA(9) = SP.Load(File.DirAssets, "dtmf_9.mp3")
   iA(10) = SP.Load(File.DirAssets, "dtmf_star.mp3")
   iA(11) = SP.Load(File.DirAssets, "dtmf_pound.mp3")

End Sub

Sub play(s As String) As Int
   Dim ch As String
   Dim count, priority As Int

   For i = 0 To s.Length -1
      ch = s.CharAt(i)
      If compare.Contains(ch) Then      
         priority = priority + 1 
      End If
   Next

   For i = 0 To s.Length -1
      ch = s.CharAt(i)
      If compare.Contains(ch) Then      
         SP.play(iA(compare.IndexOf(ch)), 1, 1, priority, 0, 1)
         Log("priority = " & priority)
         priority = priority - 1
         count = count + 1
      End If
   Next
   
   Return count 'how many tones did it play?
End Sub
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Hmmm.... get this from the unfiltered log:
B4X:
priority = 4
priority = 3
priority = 2
priority = 1
write blocked for 146 msecs, 299 delayed writes, thread 0xea80
AudioHardware pcm playback is going to standby.
Then a few seconds later I get an extra 4 lines:
B4X:
wakelock acquire, uid:1000 at elapsed real time: 529976048
wakelock acquire, uid:10062 at elapsed real time: 529976054
wakelock release, uid:1000 at elapsed real time: 529976081
wakelock release, uid:10062 at elapsed real time: 529976097

added: Oh, I don't think those last 4 lines have anything to do with it. I get them a lot it would seem.
 
Last edited:
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Your version does work well NJ plus it has the advantage of being able to specify the duration of the tone, 100milliseconds sounds good :)

Thanks!

I think I will fiddle about with it a bit.

It would be nice to get that queuing idea to work because then you could just fire off a string of sounds (or phonemes) and it would work on them leaving your app to carry on, the sound would decide the duration.

Also, I made it so that it can accept a string of anything but will pick out the numbers and the * & # in that string, as it stands the timer times even for non-playable characters but that's just a minor tweek.

Another also is that I think timers might be affecting the serial port causing the odd failed character but again it just needs some experimenting.

Your version works very well!

It should be possible to make one carefully crafted class that can have it all-ways :)
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
I do and I thank you sir :)

You'd think that the SoundPool would have methods to interrogate what was goin' on though wouldn't ya?

SP.IsThatSoundWotISentYaStillPlayinOrAzItFinishedYet?

Bit shoddy if ya ask me, I'm going to have a talk with that Mr. Google when I meet him.
 
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
of course :confused:
B4X:
Sub Play(s As String) As Int
   If s.Length = 0 Then Return 0
   inString = s
   toneCount = 0
   stepCount = 0

Log("inString = " & inString)   
   
   PlayTimer.Enabled = True
   
   Return toneCount 'how many tones did it play?
End Sub

You can't return a value!!! you can't return toneCount because PlayTimer has been enabled and sent on its wobbly way and the sub ends before any value is ever returned.

:signOops:

This isn't BASIC, its Machine Code :sign0137:

... see I 'want' BASIC but I don't want 'BASIC' in the name ;)

I'm not going to let it beat me :) Never give up! Never surrender!

hmmm... maybe MediaPlayer might be a little more forgiving?
 
Last edited:
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
after further research

I see that Steve posted about the media player having an Initialize2 method which allows it to call another sub when its finished playing!

http://www.b4x.com/forum/basic4android-getting-started-tutorials/6591-mediaplayer-tutorial-7.html

Clever chap that Steve! He comes from Swindon, they have a magic roundabout :)

I'm still a baby at it but it looks to me that SoundPool is a bit of a Fire and Forget thing for short snippets of sound and quite neat that it can play mp3s

Steve, would you happen to have an example of using the SoundPool in that queue mode?

Are there any things we can gain access to in the SoundPool by using reflection?

Looks like MediaPlayer is the way to go for anything more serious.

I did extend Erels SoundPool example ages ago:

http://www.BTInterface.com/SoundPool.zip

You need to rename it from SoundPool.zip to SoundPool.apk (my server won't allow downloads of apks)

Its rather cool

:D
 
Last edited:
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
After even furtherer research

OOP Object Oriented Programming is the way to go

or OOD as I like to call it, Object Oriented Destruction :D

And, yeah,, mmm, no I haven't got it working yet :) but I got a new error message! and that's encouraging!

And LOOK! Using Erel's objects, EO's :) I think that when you feel like this :BangHead:

You just need to add another object :)

This is what I have so far:
Object1: Class_dtmf
B4X:
'Class module
Sub Class_Globals
   Dim meFiles(12) As String
End Sub

'InitmeFileslizes the object. You can add parameters to this method if needed.
Public Sub Initialize
   meFiles(0) = "dtmf_0.mp3"
   meFiles(1) = "dtmf_1.mp3"
   meFiles(2) = "dtmf_2.mp3"
   meFiles(3) = "dtmf_3.mp3"
   meFiles(4) = "dtmf_4.mp3"
   meFiles(5) = "dtmf_5.mp3"
   meFiles(6) = "dtmf_6.mp3"
   meFiles(7) = "dtmf_7.mp3"
   meFiles(8) = "dtmf_8.mp3"
   meFiles(9) = "dtmf_9.mp3"
   meFiles(10) = "dtmf_star.mp3"
   meFiles(11) = "dtmf_pound.mp3"
End Sub

Sub Play(inString As String) As Int
   Dim ch As String 'the character in inString 
    Dim count, diditwork As Int
   Dim no_REallyPlayIt As Class_play
   no_REallyPlayIt.Initialize
   Dim compare As String = "0123456789*#"

    For i = 0 To inString.Length -1
        ch = inString.CharAt(i)
        If compare.Contains(ch) Then
         no_REallyPlayIt.Set_Load(meFiles(compare.IndexOf(ch)))
         diditwork = no_REallyPlayIt.Play
         Log(diditwork)
         If diditwork <> 0 Then
               count = count + 1
         End If
        End If
    Next
    
    Return count 'how many tones did it play?

End Sub

and here's my new object: Class_play
B4X:
'Class module
Sub Class_Globals
   Dim SP As SoundPool
   Private LoadedFileID As Int
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
   SP.Initialize(1)
End Sub

Sub Set_Load(fileToLoad As String) 'the object's property Load ... oooh!
   LoadedFileID = SP.Load(File.DirAssets, fileToLoad)
   Log(fileToLoad) 'that's working
End Sub

Sub Play() As Int
   
   Return SP.Play(LoadedFileID, 1, 1, 1, 0, 1)

End Sub

so apart from that it doesn't quite work yet I'm actually beginning to see the light :) Look at how much simpler it is! Look at those globals that slipped down into the main body of the code!

A bit of help would be appreciated but I'm actually beginning to enjoy my Object Oriented Destruction!

Oh... and the new error message:
B4X:
dtmf_6.mp3


  sample 1 not READY
0
dtmf_7.mp3
  sample 2 not READY


0
dtmf_8.mp3
  sample 3 not READY
0
dtmf_9.mp3
  sample 4 not READY
0

Hmmmm!!!! Curioser and Curioser!
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
Hi,

Sorry I've not been about this evening, had to work in Leamington Spa (no mobile internet connection at the Venue) so I couldn't keep up.

I am 99.9% certain that I got the queue working, I suggested it to someone else a while ago and I'm sure it worked. I'll give it another go. And nothing more available in the API than is already exposed see here.

Now about the Sample not ready, when you load a sound into Soundpool, if it's compressed, it uncompresses it a load time. So there may be a time when you try to play it and the sample is not loaded, especially if you load a lot or large files.

We can check by using the fact that the play method returns a 0 if it fails. So we can do something like:

B4X:
Sub InitializeSP
   Dim Success As Int
   For i = 0 To noSamples-1
      Success = 0
      Do While Success = 0
         Success = SP.Play(LoadIDs(i),0,0,1,0,1)
      Loop
   Next

End Sub

And then we know our sounds are ready to play, hopefully getting rid of your error message.

Ahh yes the magic roundabout, easy when you know how.
 
Last edited:
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
Very intense, very intense,

loosen up

its not a stopping thing

I'm going through a learning curve, I'll get there.

I like EO's

'Erel's Objects'

When I was writing Object number two there was a natural tendency to take the global variables from Object number one and take them down to the body of the code instead.

Such a nice community of helpful souls, think I'll stay here a while until I become....

YOU WILL BE ASSIMILATED! RESISTANCE IS FUTILE..

too late eek!
 
Upvote 0

stevel05

Expert
Licensed User
Longtime User
OK found it, file attached.

The SP.Play's needed to be in loops too as subsequent plays failed (we only let it play one sound at a time) until the previous one finished. Wheew, I knew I wasn't going totally mad. Not quite yet anyway.
 

Attachments

  • SPTest.zip
    88.7 KB · Views: 199
Upvote 0

IanMc

Well-Known Member
Licensed User
Longtime User
WE ARE THE BORG

YOU WILL BE ASSIMILATED

RESISTANCE IS FUTILE

and ...

thanks for that, I'll take a look :)
 
Upvote 0
Top