に indicates target/state/result of change here. 声に出す is close to 声にして出す "to output in the state/form of voice", i.e., "to say/express in voice" as a set.
As bentenmusume-san explained perfectly, 声に出す usually used in a situation where the subject feels/thinks something inside them/in their mind, so expressions like 怒りを声に出す or 不満を声に出す can work. には is an emphasized form of it.
On the other hand, in 声を出す, the contents of 声 is not always important. 声 might be meaningless sounds like a groan, for instance, 気絶していて全く声を出さない. 声に出さない doesn't work in this case.
Furthermore, 声に出す also means "to put emotion into voice", thus, 声に出さない is also used in a situation where the speaker says words without the emotion. In your example, the subject might say something, but they didn't show their surprise in their voice. 声を出さない doesn't work in this case, needless to say.