Keep it simple. I think modelling echos would be strange rather than fun. Unless you see some situation where this helps?
For each cell, calculate how much does it block sounds (attenuation). And then to see whether the sound at X is heard at Y, find the path between X and Y which has the smallest total amount of attenuation, and let the sound be heard be blocked if this total is smaller than the threshold. (I have used that myself for echolocation (a bat can sense all locations for which the total attenuation is smaller than threshold) and I think it works very well.)
That's similar to what corremn said, but A* is a bad choice for algorithm, IMO. If you want (A) only player be able to hear monsters (and/or vice versa), precalculate attenuation between player and each location in the dungeon whenever the player position or the dungeon changes. Since you do this for each target location, Dijkstra algorithm would work well for that. If you want (B) monsters to hear each other, use Dijkstra whenever a sound event happens to determine other monsters which can hear it. In both cases, Dijkstra is easier to implement than A*, and more effective (in case (A), since you then precalculate only once per player move (unless you model changes in attenuation due to monster movement), and you probably would have to use Dijkstra anyway to make monsters able to hear the player, and in (B), since you perform a algorithm better suited to multiple targets).