1509 |
1509 |
1510 playSineF32:freq forSeconds:nSeconds |
1510 playSineF32:freq forSeconds:nSeconds |
1511 "output some tone for some time |
1511 "output some tone for some time |
1512 in F32 audioFormat - a test method" |
1512 in F32 audioFormat - a test method" |
1513 |
1513 |
1514 |buffer numSamples val scale isUnsigned restSamples| |
1514 |buffer numSamples val scale restSamples numChannels| |
1515 |
1515 |
1516 (audioFormat == #F32) ifFalse:[ |
1516 (audioFormat == #F32) ifFalse:[ |
1517 self error:'must be in float mode' mayProceed:true. |
1517 self error:'must be in float mode' mayProceed:true. |
1518 ]. |
1518 ]. |
1519 |
1519 |
1520 numSamples := self sampleRate. |
1520 numSamples := self sampleRate. |
1521 buffer := FloatArray new:numSamples. |
1521 numChannels := self numberOfChannels. |
|
1522 buffer := FloatArray new:(numSamples * numChannels). |
1522 |
1523 |
1523 "fill it with a sine wave" |
1524 "fill it with a sine wave" |
1524 |
1525 |
1525 scale := freq * 2 * (Float pi). |
1526 scale := freq * 2 * (Float pi). |
1526 1 to:numSamples do:[:i | |
1527 numChannels == 2 ifTrue:[ |
1527 val := (scale * i / self sampleRate) sin. |
1528 1 to:numSamples do:[:i | |
1528 buffer at:i put:val |
1529 val := (scale * i / self sampleRate) sin. |
|
1530 buffer at:(i-1)*2 put:val. |
|
1531 buffer at:(i-1)*2+1 put:0. |
|
1532 ]. |
|
1533 ] ifFalse:[ |
|
1534 1 to:numSamples do:[:i | |
|
1535 val := (scale * i / self sampleRate) sin. |
|
1536 buffer at:i put:val |
|
1537 ]. |
1529 ]. |
1538 ]. |
1530 |
1539 |
1531 1 to:nSeconds truncated do:[:s | |
1540 1 to:nSeconds truncated do:[:s | |
1532 self nextPutBytes:(numSamples*4) from:buffer startingAt:1 |
1541 self nextPutBytes:(numSamples*4) from:buffer startingAt:1 |
1533 ]. |
1542 ]. |
1534 restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated. |
1543 restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated. |
1535 restSamples > 0 ifTrue:[ |
1544 restSamples > 0 ifTrue:[ |
1536 self nextPutBytes:(restSamples*4) from:buffer startingAt:1 |
1545 self nextPutBytes:(restSamples*4) from:buffer startingAt:1 |
1537 ]. |
1546 ]. |
1538 |
1547 |
1539 " |
1548 " |
1540 SoundStream writing setAudioFormat:#F32; playSineF32:440 forSeconds:2; close |
1549 SoundStream writing setAudioFormat:#F32; playSineF32:440 forSeconds:2; close |
1541 SoundStream writing setAudioFormat:#F32; playSineF32:880 forSeconds:2; close |
1550 SoundStream writing setAudioFormat:#F32; playSineF32:880 forSeconds:2; close |
1585 "output some tone for nSeconds in S16 audioFormat - a test method" |
1594 "output some tone for nSeconds in S16 audioFormat - a test method" |
1586 |
1595 |
1587 |buffer numSamples val scale restSamples| |
1596 |buffer numSamples val scale restSamples| |
1588 |
1597 |
1589 (audioFormat startsWith:#U16) ifFalse:[ |
1598 (audioFormat startsWith:#U16) ifFalse:[ |
1590 (audioFormat startsWith:#S16) ifFalse:[ |
1599 (audioFormat startsWith:#S16) ifFalse:[ |
1591 self error:'must be in 16bit mode' mayProceed:true. |
1600 self error:'must be in 16bit mode' mayProceed:true. |
1592 ^ self |
1601 ^ self |
1593 ] |
1602 ] |
1594 ]. |
1603 ]. |
1595 |
1604 |
1596 "allocate memory for 1 sec playing time" |
1605 "allocate memory for 1 sec playing time" |
1597 numSamples := self sampleRate. |
1606 numSamples := self sampleRate. |
1598 buffer := WordArray new:numSamples. |
1607 buffer := WordArray new:numSamples. |
1599 |
1608 |
1600 "fill it with a sine wave" |
1609 "fill it with a sine wave" |
1601 |
1610 |
1602 scale := freq * 2 * (Float pi). |
1611 scale := freq * 2 * (Float pi). |
1603 1 to:numSamples do:[:i | |
1612 1 to:numSamples do:[:i | |
1604 val := (scale * i / numSamples) sin. |
1613 val := (scale * i / numSamples) sin. |
1605 val := (val * 16r7FFF) rounded. |
1614 val := (val * 16r7FFF) rounded. |
1606 audioFormat == #U16 ifTrue:[ |
1615 audioFormat == #U16 ifTrue:[ |
1607 val := val + 16r8000 |
1616 val := val + 16r8000 |
1608 ]. |
1617 ]. |
1609 buffer at:i put:(val bitAnd:16rFFFF) |
1618 buffer at:i put:(val bitAnd:16rFFFF) |
1610 ]. |
1619 ]. |
1611 |
1620 |
1612 1 to:nSeconds truncated do:[:s | |
1621 1 to:nSeconds truncated do:[:s | |
1613 self nextPutBytes:(numSamples*2) from:buffer startingAt:1 |
1622 self nextPutBytes:(numSamples*2) from:buffer startingAt:1 |
1614 ]. |
1623 ]. |
1615 restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated. |
1624 restSamples := ((nSeconds - nSeconds truncated) * numSamples) truncated. |
1616 restSamples > 0 ifTrue:[ |
1625 restSamples > 0 ifTrue:[ |
1617 self nextPutBytes:(restSamples*2) from:buffer startingAt:1 |
1626 self nextPutBytes:(restSamples*2) from:buffer startingAt:1 |
1618 ]. |
1627 ]. |
1619 |
1628 |
1620 "of course, the frequency should be below half the |
1629 "of course, the frequency should be below half the |
1621 sampleRate - hear below ... |
1630 sampleRate - hear below ... |
1622 |
1631 |
1623 SoundStream writing setSampleRate:4000; tuneTone16:440; close |
1632 SoundStream writing setSampleRate:4000; tuneTone16:440 seconds:1; close |
1624 SoundStream writing setSampleRate:8000; tuneTone16:440; close |
1633 SoundStream writing setSampleRate:8000; tuneTone16:440 seconds:1; close |
1625 SoundStream writing setSampleRate:10000; tuneTone16:440; close |
1634 SoundStream writing setSampleRate:10000; tuneTone16:440 seconds:1; close |
1626 SoundStream writing setSampleRate:20000; tuneTone16:440; close |
1635 SoundStream writing setSampleRate:20000; tuneTone16:440 seconds:1; close |
1627 SoundStream writing setSampleRate:40000; tuneTone16:440; close |
1636 SoundStream writing setSampleRate:40000; tuneTone16:440 seconds:1; close |
|
1637 SoundStream writing tuneTone16:440 seconds:1; close |
1628 " |
1638 " |
1629 |
1639 |
1630 "Modified: / 21.12.1998 / 09:11:30 / cg" |
1640 "Modified: / 21.12.1998 / 09:11:30 / cg" |
1631 ! |
1641 ! |
1632 |
1642 |
3437 "normally not reached" |
3447 "normally not reached" |
3438 ^ nil. |
3448 ^ nil. |
3439 ]. |
3449 ]. |
3440 self registerForFinalization. |
3450 self registerForFinalization. |
3441 |
3451 |
|
3452 ! |
|
3453 |
|
3454 reopenStream |
|
3455 |ok errorStringOrNil error| |
|
3456 |
|
3457 %{ |
|
3458 #ifdef SUPPORT_PORTAUDIO |
|
3459 static PaStreamParameters outputParameters; |
|
3460 PaStream *stream; |
|
3461 PaError paErr; |
|
3462 struct paStreamData* paStreamData; |
|
3463 int nChannels, sampleRate, bytesPerSample; |
|
3464 # define FRAMES_PER_BUFFER 128 |
|
3465 |
|
3466 ok = false; |
|
3467 |
|
3468 /* default output device */ |
|
3469 outputParameters.device = Pa_GetDefaultOutputDevice(); |
|
3470 if (outputParameters.device == paNoDevice) { |
|
3471 fprintf(stderr, "SoundStream [warning]: No default output device.\n"); |
|
3472 errorStringOrNil = __MKSTRING("No default output device"); |
|
3473 goto out; |
|
3474 } |
|
3475 |
|
3476 if (__isSmallInteger(__INST(numberOfChannels))) { |
|
3477 nChannels = __intVal(__INST(numberOfChannels)); |
|
3478 } else { |
|
3479 nChannels = 1; |
|
3480 } |
|
3481 outputParameters.channelCount = nChannels; |
|
3482 outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; |
|
3483 outputParameters.hostApiSpecificStreamInfo = NULL; |
|
3484 |
|
3485 // The standard formats paFloat32, paInt16, paInt32, paInt24, paInt8 |
|
3486 // and aUInt8 are usually implemented by all implementations. |
|
3487 // The floating point representation (paFloat32) uses +1.0 and -1.0 as the |
|
3488 // maximum and minimum respectively. |
|
3489 // paUInt8 is an unsigned 8 bit format where 128 is considered "ground" |
|
3490 |
|
3491 if (__INST(audioFormat) == @symbol(S16)) { |
|
3492 outputParameters.sampleFormat = paInt16; |
|
3493 bytesPerSample = 2; |
|
3494 } else if (__INST(audioFormat) == @symbol(S32)) { |
|
3495 outputParameters.sampleFormat = paInt32; |
|
3496 bytesPerSample = 4; |
|
3497 } else if (__INST(audioFormat) == @symbol(S24)) { |
|
3498 outputParameters.sampleFormat = paInt24; |
|
3499 bytesPerSample = 3; |
|
3500 } else if (__INST(audioFormat) == @symbol(S8)) { |
|
3501 outputParameters.sampleFormat = paInt8; |
|
3502 bytesPerSample = 1; |
|
3503 } else if (__INST(audioFormat) == @symbol(F32)) { |
|
3504 outputParameters.sampleFormat = paFloat32; |
|
3505 bytesPerSample = 4; |
|
3506 } else if (__INST(audioFormat) == @symbol(U8)) { |
|
3507 outputParameters.sampleFormat = paUInt8; |
|
3508 bytesPerSample = 1; |
|
3509 } else { |
|
3510 fprintf(stderr, "SoundStream [warning]: unknown format - using U8\n"); |
|
3511 outputParameters.sampleFormat = paUInt8; |
|
3512 bytesPerSample = 1; |
|
3513 } |
|
3514 |
|
3515 if (__isSmallInteger(__INST(sampleRate))) { |
|
3516 sampleRate = __intVal(__INST(sampleRate)); |
|
3517 } else { |
|
3518 fprintf(stderr, "SoundStream [warning]: using default sampleRate 8000\n"); |
|
3519 sampleRate = 8000; |
|
3520 } |
|
3521 |
|
3522 paStreamData = (struct paStreamData*)malloc(sizeof(struct paStreamData)); |
|
3523 if (paStreamData == NULL) { |
|
3524 fprintf(stderr, "SoundStream [warning]: failed to allocate paStream\n"); |
|
3525 errorStringOrNil = __MKSTRING("failed to allocate paStream"); |
|
3526 goto out; |
|
3527 } |
|
3528 |
|
3529 paErr = Pa_OpenStream( |
|
3530 &stream, |
|
3531 NULL, /* no input */ |
|
3532 &outputParameters, |
|
3533 sampleRate, |
|
3534 FRAMES_PER_BUFFER, |
|
3535 paClipOff, /* we won't output out of range samples so don't bother clipping them */ |
|
3536 paCallback, |
|
3537 paStreamData ); |
|
3538 |
|
3539 if (paErr != paNoError) { |
|
3540 fprintf(stderr, "SoundStream [warning]: openStream: %s\n", Pa_GetErrorText( paErr )); |
|
3541 free(paStreamData); |
|
3542 errorStringOrNil = __MKSTRING(Pa_GetErrorText( paErr )); |
|
3543 goto out; |
|
3544 } |
|
3545 paStreamData->stream = stream; |
|
3546 |
|
3547 paStreamData->readOffset = 0; |
|
3548 paStreamData->bytesPerSample = bytesPerSample; |
|
3549 paStreamData->nChannels = nChannels; |
|
3550 |
|
3551 paStreamData->currentBuffer = NULL; |
|
3552 paStreamData->lastBuffer = NULL; |
|
3553 paStreamData->freeList = NULL; |
|
3554 paStreamData->hasFinished = 0; |
|
3555 |
|
3556 ok = true; |
|
3557 out:; |
|
3558 #endif /* SUPPORT_PORTAUDIO */ |
|
3559 |
|
3560 %}. |
|
3561 ok == false ifTrue:[ |
|
3562 lastErrorString := errorStringOrNil. |
|
3563 lastErrorNumber := error ? -1. |
|
3564 self openError:error. |
|
3565 "normally not reached" |
|
3566 ^ nil. |
|
3567 ]. |
3442 ! ! |
3568 ! ! |
3443 |
3569 |
3444 !SoundStream::PortAudio methodsFor:'private'! |
3570 !SoundStream::PortAudio methodsFor:'private'! |
3445 |
3571 |
3446 initialize |
3572 initialize |
3451 (IsInitialized ? false) ifFalse:[ |
3577 (IsInitialized ? false) ifFalse:[ |
3452 self class primitiveInitializeDevice |
3578 self class primitiveInitializeDevice |
3453 ]. |
3579 ]. |
3454 |
3580 |
3455 "Created: 17.11.1995 / 17:28:14 / cg" |
3581 "Created: 17.11.1995 / 17:28:14 / cg" |
|
3582 ! |
|
3583 |
|
3584 setAudioFormat:aSymbol |
|
3585 super setAudioFormat:aSymbol. |
|
3586 self reopenStream |
|
3587 ! |
|
3588 |
|
3589 setChannels:numChannels |
|
3590 super setChannels:numChannels. |
|
3591 self reopenStream |
|
3592 ! |
|
3593 |
|
3594 setFragmentSize:blockSize |
|
3595 super setFragmentSize:blockSize. |
|
3596 self reopenStream |
|
3597 ! |
|
3598 |
|
3599 setSampleRate:hz |
|
3600 super setSampleRate:hz. |
|
3601 self reopenStream |
3456 ! ! |
3602 ! ! |
3457 |
3603 |
3458 !SoundStream::PortAudio methodsFor:'queries'! |
3604 !SoundStream::PortAudio methodsFor:'queries'! |
3459 |
3605 |
3460 supportedAudioFormats |
3606 supportedAudioFormats |