I am trying to write an audio file using python's wave
and numpy
. So far I have the following and it works well:
import wave
import numpy as np
# set up WAV file parameters
num_channels = 1 # mono audio
sample_width = 1 # 8 bits(1 byte)/sample
sample_rate = 44.1e3 # 44.1k samples/second
frequency = 440 # 440 Hz
duration = 20 # play for this many seconds
num_samples = int(sample_rate * duration) # samples/seconds * seconds
# open WAV file and write data
with wave.open('sine8bit_2.wav', 'w') as wavfile:
wavfile.setnchannels(num_channels)
wavfile.setsampwidth(sample_width)
wavfile.setframerate(sample_rate)
t = np.linspace(0, duration, num_samples)
data = (127*np.sin(2*np.pi*frequency*t)).astype(np.int8)
wavfile.writeframes(data) # or data.tobytes() ??
My issue is that since I am using a high sampling rate, the num_samples
variable might quickly become too large (9261000 samples for a 3 minute 30 seconds track say). Would using a numpy array this large be advisable? Is there a better way of going about this? Also is use of writeframes(.tobytes())
needed in this case because my code runs fine without it and it seems like extra overhead (especially if the arrays get too large).
CodePudding user response:
It is generally not advisable to use a NumPy array that is too large because it can consume a lot of memory and make your program slow or even crash. In this case, you could try to write the data to the WAV file in smaller chunks, rather than all at once, to reduce the memory usage.
To do this, you can use a loop and write a fixed number of samples to the file at each iteration. For example:
chunk_size = 10000
for i in range(0, num_samples, chunk_size):
chunk = data[i:i chunk_size]
wavfile.writeframes(chunk)
This will write the data in chunks of size chunk_size to the WAV file. You can adjust the value of chunk_size to find a balance between memory usage and performance.
Regarding the use of writeframes(.tobytes()), the writeframes() method expects a bytes-like object as its input, so you can either pass it data directly or use data.tobytes() to convert it to a bytes object first. Both should work, but using data.tobytes() might be slightly slower due to the extra conversion step.
CodePudding user response:
hey so i wanted to help you a little more on the subject
To make the data array smaller, you can either reduce the duration of the audio or the sampling rate.
For example, to reduce the duration, you can simply decrease the value of the duration variable. This will reduce the number of samples in the data array and make it smaller.
To reduce the sampling rate, you can decrease the value of the sample_rate variable. This will also reduce the number of samples in the data array, but it might also affect the quality of the audio. A lower sampling rate means that fewer samples are taken per second, which can result in a lower quality audio signal.
You can try experimenting with different values for these variables to find a balance between the size of the data array and the quality of the audio.
I hope this helps! Let me know if you have any more questions.
CodePudding user response:
Assuming you are only going to write a sine wave, you could very well create only one period as your data
array and write that several times to the .wav
file.
Using the parameters you provided, your data
array is 8800 times smaller with that approach. Its size also no longer depends on the duration of your file!
import wave
import numpy as np
# set up WAV file parameters
num_channels = 1 # mono audio
sample_width = 1 # 8 bits(1 byte)/sample
sample_rate = 44.1e3 # 44.1k samples/second
frequency = 440 # 440 Hz
duration = 20 # play for this many seconds
# Create a single period of sine wave.
n = round(sample_rate/frequency)
t = np.linspace(0, 1/frequency, n)
data = (127*np.sin(2*np.pi*frequency*t)).astype(np.int8)
periods = round(frequency*duration)
# open WAV file and write data
with wave.open('sine8bit_2.wav', 'w') as wavfile:
wavfile.setnchannels(num_channels)
wavfile.setsampwidth(sample_width)
wavfile.setframerate(sample_rate)
for _ in range(periods):
wavfile.writeframes(data)